【问题标题】:Uploading file to server throws out of memory exception将文件上传到服务器会引发内存不足异常
【发布时间】:2014-04-17 19:43:15
【问题描述】:

我正在尝试使用 asp.net web api 实现文件上传系统,但遇到了问题。我正在尝试将多部分表单数据放入内存流中,以便可以根据服务层实现将其写入磁盘或 blob 存储。问题是它适用于小文件,但我正在尝试上传 291 MB 的文件并且它抛出内存不足异常。代码如下:

if (!Request.Content.IsMimeMultipartContent())
{
    Request.CreateErrorResponse(HttpStatusCode.UnsupportedMediaType, "Request must be multipart.");
}

var provider = new MultipartMemoryStreamProvider();

try
{
    await Request.Content.ReadAsMultipartAsync(provider);

    var infoPart = provider.Contents.Where(x => x.Headers.ContentDisposition.Name.Replace("\"", string.Empty) == "fileInfo").SingleOrDefault();
    var filePart = provider.Contents.Where(x => x.Headers.ContentDisposition.Name.Replace("\"", string.Empty) == "filePart" && x.Headers.ContentDisposition.FileName != null).Single();
    byte[] file = null;

    using (Stream stream = filePart.ReadAsStreamAsync().Result)
    {
        using (MemoryStream memory = new MemoryStream())
        {
            stream.CopyTo(memory);
            file = memory.ToArray();
        }
    }

    string fileContentType = filePart.Headers.ContentType.MediaType;

    FileDto result = _fileService.AddFileToResource(Variables);
    string uri = Url.Link("DefaultGet", new { id = result.ID });
    return Request.CreateResponse(HttpStatusCode.OK);

引发错误的部分在

await Request.Content.ReadAsMultipartAsync(provider);

确切的错误是

将 MIME 多部分正文部分写入输出流时出错。

内部例外

引发了“System.OutOfMemoryException”类型的异常。

我已尝试创建自定义BufferPolicySelector,如this post 和许多其他地方的第二个答案所示,但这似乎根本没有帮助。

我也添加到了我的 web.config:

<httpRuntime targetFramework="4.5" maxRequestLength="307200"/>

<security>
  <requestFiltering>
    <requestLimits maxAllowedContentLength="367001600"/>
  </requestFiltering>
</security>

【问题讨论】:

  • 我在这里可能是错的,但是我阅读您的代码的方式,您将在这个 sn-p 的内存中拥有该文件的 3 个副本:请求内容流、内存流和字节数组文件。也许 reuqest 结果流将真正流式传输,但“内存”和“文件”将同时在 RAM 中。
  • 感谢您的建议。我将如何改善这一点?它并没有真正帮助我的问题,因为它在读取 await Request.Content.ReadAsMultipartAsync(provider); 中的内容时会引发错误。但我绝对想了解如何使其尽可能高效
  • 还要验证 maxRequestLength 应该大于或等于分配给 RequestLengthDiskThreshold 的值
  • 是的,我这样做了,但是没有用
  • @gmoney12 很难为您重构它,因为很难说出您在其中到底在做什么。 'stream' 和 'file' 似乎没有被使用,因为我可以看到整个块可以被删除。

标签: c# asp.net asp.net-web-api


【解决方案1】:

一种解决方案是使用MultipartFormDataStreamProvider 而不是MultipartMemoryStreamProvider 以避免在调用期间出现内存不足异常

Request.Content.ReadAsMultipartAsync(..)

我在读取大文件 (> 100 MB) 的 MultiPart 文件内容时尝试使用 MemoryStreamProvider 时遇到了类似的问题。对我有用的解决方法是使用MultipartFormDataStreamProvider。该文件在 ReadAsMultipartAsync 调用期间被写入磁盘,如果您需要将其加载回内存中,可以稍后再加载。

下面是一个例子:

Sending HTML Form Data in Web API: File Upload and Multipart MIME

    string root = HttpContext.Current.Server.MapPath("~/App_Data");
    var provider = new MultipartFormDataStreamProvider(root);

    try
    {
        // Read the form data.
        await Request.Content.ReadAsMultipartAsync(provider);

        // This illustrates how to get the file names.
        foreach (MultipartFileData file in provider.FileData)
        {
            Trace.WriteLine(file.Headers.ContentDisposition.FileName);
            Trace.WriteLine("Server file path: " + file.LocalFileName);
        }
        return Request.CreateResponse(HttpStatusCode.OK);
    }
    catch(...)

【讨论】:

  • 这就是我最终要做的。不确定这是否是最好的方法,但我永远无法弄清楚如何让它以另一种方式工作
  • 网址不存在了。
  • 有没有办法在 asp.net core 中做到这一点?我只有 Request.Body 没有 Request.Content 也没有 ReadAsMultipartAsync 方法。
【解决方案2】:

检查您的网络配置文件上传限制

<system.webServer>
        <security>
            <requestFiltering>
                <requestLimits maxAllowedContentLength="524288000"/>
            </requestFiltering>
        </security>
</system.webServer>

来自 OP 编辑​​:

<httpRuntime targetFramework="4.5" maxRequestLength="307200"/>

maxRequestLength 以 Kb 为单位 - 这是缓冲阈值,您正在请求 300MB 的缓冲区 - 可能不好。把它调低

【讨论】:

  • 我已经在 web.config 中找到了,我将对其进行编辑以添加该信息
  • 请记住,上面的值是以字节为单位的,最大值为 UINT,因此大约为 4GB。上面的值大约是500MB
  • 对,但我只上传了一个 290 MB 的文件,所以它应该可以上传吧?还是我想错了?
  • 嗯,这就是为什么我不明白为什么这不起作用,因为我的最大内容长度为 300 MB,我什至尝试像您一样将其设置为 500 MB,但它仍然会导致内存不足界限
  • 这只是对请求的内容大小限制的设置,但是你的问题是你的进程内存不足
【解决方案3】:

验证服务器上文件夹的权限。

或者

例如,将 httpRuntime maxRequestLength 放在 web.config 中:

<httpRuntime maxRequestLength="5242880" />

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-02-16
    • 1970-01-01
    • 2014-06-17
    • 1970-01-01
    • 2019-07-24
    相关资源
    最近更新 更多