【问题标题】:Receiving an out of memory exception with memorystream使用 memorystream 接收内存不足异常
【发布时间】:2012-11-05 21:05:40
【问题描述】:

我正在尝试编写一个可以与开源媒体管理平台 Kaltura 一起使用的应用程序。 Kaltura 提供了一些与他们的 Web API 对话的 C# 客户端库,我已经能够与服务器对话并成功上传视频。我遇到的问题是,一旦文件达到一定大小,我就会收到内存不足异常并且程序崩溃。我想尝试解决这个问题并将改进的代码提交回开源项目,但是对于 C# 来说是新手,我不知道从哪里开始。有没有比 memorystream 更好的方法来做他们正在做的事情?

提前致谢。

//Problematic code

private void PostMultiPartWithFiles(HttpWebRequest request, KalturaParams kparams, KalturaFiles kfiles)
    {
        string boundary = "---------------------------" + DateTime.Now.Ticks.ToString("x");
        request.ContentType = "multipart/form-data; boundary=" + boundary;

        // use a memory stream because we don't know the content length of the request when we have multiple files
        MemoryStream memStream = new MemoryStream();


        byte[] buffer;
        int bytesRead = 0;

        StringBuilder sb = new StringBuilder();
        sb.Append("--" + boundary + "\r\n");
        foreach (KeyValuePair<string, string> param in kparams)
        {
            sb.Append("Content-Disposition: form-data; name=\"" + param.Key + "\"" + "\r\n");
            sb.Append("\r\n");
            sb.Append(param.Value);
            sb.Append("\r\n--" + boundary + "\r\n");
        }

        buffer = Encoding.UTF8.GetBytes(sb.ToString());
        memStream.Write(buffer, 0, buffer.Length);

        foreach (KeyValuePair<string, FileStream> file in kfiles)
        {
            sb = new StringBuilder();
            FileStream fileStream = file.Value;
            sb.Append("Content-Disposition: form-data; name=\"" + file.Key + "\"; filename=\"" + Path.GetFileName(fileStream.Name) + "\"" + "\r\n");
            sb.Append("Content-Type: application/octet-stream" + "\r\n");
            sb.Append("\r\n");

            // write the current string builder content
            buffer = Encoding.UTF8.GetBytes(sb.ToString());
            memStream.Write(buffer, 0, buffer.Length);

            // write the file content
            buffer = new Byte[checked((uint)Math.Min(4096, (int)fileStream.Length))];
            bytesRead = 0;
            while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) != 0)
                memStream.Write(buffer, 0, bytesRead);

            buffer = Encoding.UTF8.GetBytes("\r\n--" + boundary + "\r\n");
            memStream.Write(buffer, 0, buffer.Length);
        }

        request.ContentLength = memStream.Length;

        Stream requestStream = request.GetRequestStream();
        // write the memorty stream to the request stream
        memStream.Seek(0, SeekOrigin.Begin);
        buffer = new Byte[checked((uint)Math.Min(4096, (int)memStream.Length))];
        bytesRead = 0;
        while ((bytesRead = memStream.Read(buffer, 0, buffer.Length)) != 0)
            requestStream.Write(buffer, 0, bytesRead);

        requestStream.Close();
        memStream.Close();
    }

【问题讨论】:

  • 看起来您正在将整组视频加载到您的内存流中...这肯定会导致您的内存不足异常。您不应将其全部缓冲到内存中,而应直接将其复制到 RequestStream
  • 这是某种 POST 操作吗?显而易见的问题是代码不直接使用请求流,而是绕道而行。大文件肯定会失败。因此,不要写入 Memorystream,而是写入请求流,然后看看它会带来什么。

标签: c# memory-management kaltura


【解决方案1】:

这或多或少是我将如何编写它的一个版本。它只编译,但我还没有测试过。注意使用StreamWriter和直接使用请求流...

public class SendStuff
    {
        private readonly HttpWebRequest _request;
        private readonly Dictionary<string, string> _kparams;
        private readonly Dictionary<string, FileStream> _kfiles;
        readonly string _boundary = "---------------------------" + DateTime.Now.Ticks.ToString("x");

        public SendStuff(
            HttpWebRequest request, 
            Dictionary<string, string> kparams, 
            Dictionary<string, FileStream> kfiles)
        {
            _request = request;
            _kparams = kparams;
            _kfiles = kfiles;
            _request.ContentType = "multipart/form-data; boundary=" + _boundary;
        }

        public void Do()
        {

            // Based on HTTP 1.1, if the server can determine the content length, it need not insist on
            // us sending one. If you are talking
            // to a "special" server, construct the headers beforehand, measure their length
            // and identify the file lengths of the files to be sent.

            using (var reqStream = _request.GetRequestStream())
            using (var writer = new StreamWriter(reqStream))
            {
                writer.NewLine = "\r\n";
                WriteBoundary(writer);
                WriteParams(writer);

                foreach (var file in _kfiles)
                {
                    writer.WriteLine("Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"", 
                        file.Key, 
                        Path.GetFileName(file.Value.Name));
                    writer.WriteLine("Content-Type: application/octet-stream");
                    writer.WriteLine();

                    WriteTheFileContent(reqStream, file.Value);

                    WriteBoundary(writer);
                }
            }
        }

        private static void WriteTheFileContent(Stream reqStream, Stream fileStream)
        {
            int bytesRead;
            var buffer = new byte[4096];
            while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) != 0)
                reqStream.Write(buffer, 0, bytesRead);
        }

        private void WriteParams(StreamWriter writer)
        {
            foreach (var param in _kparams)
            {
                writer.WriteLine("Content-Disposition: form-data; name=\"{0}\"", param.Key);
                writer.WriteLine();
                writer.WriteLine(param.Value);
                WriteBoundary(writer);
            }
        }

        private void WriteBoundary(TextWriter writer)
        {
            writer.WriteLine("\r\n--{0}\r\n", _boundary);
        }
    }

【讨论】:

  • 谢谢。这很有帮助。尽管我认为发送所有内容的方式有问题,但我已经能够传输数据。不过,我最终应该能够得到它。
猜你喜欢
  • 2013-06-22
  • 2010-10-05
  • 1970-01-01
相关资源
最近更新 更多