3

我在我的控制器上有一个动作,应该上传大(500mb - 2gb)文件。例如,

[HttpPost]
public void PostFile([FromUri]Guid uploadId)
{
  ...
}

在正文中,会执行一些检查(例如uploadId 是否存在),然后将HTTP 请求的发布正文写入磁盘(/network share)。

我以为我已经解决了这个问题,因为如果没有创建 uploadId,我可以通过抛出 HttpResponseException 几乎立即拒绝 HTTP 请求,但我刚刚发现即使在这种情况下,请求的正文也会完全上传到服务器并将 ASP.Net Temporary Files 文件夹保存到磁盘。这意味着客户端必须完全上传文件(大约需要 5-10 分钟)才能发现它已被服务器拒绝。

我该如何避免这种情况?有什么方法可以防止 IIS 将正文写入磁盘,并允许控制器处理流?我发现客户端开始上传和请求命中我自己的代码之间有 5 分钟的延迟令人沮丧,因为 ASP.Net 必须做自己的事情并将上传写入本地磁盘。


编辑

有人问我如何读取上传数据。本质上,使用这种方法:

        /// <summary>
        /// Saves the current request's upload stream to the proivded path.
        /// </summary>
        /// <param name="destinationPath">The path to save to.</param>
        /// <returns></returns>
        private async Task<FileInfo> SaveUploadToDiskAsync(string destinationPath)
        {
            using (var httpContent = Request.Content)
            {
                using (FileStream fileStream = new FileStream(destinationPath, FileMode.Create, FileAccess.Write, FileShare.None, 8192, true))
                {
                    await httpContent.CopyToAsync(fileStream);

                    return new FileInfo(destinationPath);
                }
            }
        }
4

2 回答 2

3

您可以通过实现自定义控制器,将 Web API 配置为在特定控制器(或全局,就此而言)以无缓冲模式(流式传输)工作WebHostBufferPolicySelector

public class NoBufferPolicySelector : WebHostBufferPolicySelector
{
   public override bool UseBufferedInputStream(object hostContext)
   {
      var context = hostContext as HttpContextBase;

      if (context != null)
      {
         if (string.Equals(context.Request.RequestContext.RouteData.Values["controller"].ToString(), "upload", StringComparison.InvariantCultureIgnoreCase))
            return false;
      }

      return true;
   }

   public override bool UseBufferedOutputStream(HttpResponseMessage response)
   {
      return base.UseBufferedOutputStream(response);
   }
}

我已经为在 Web API中处理大文件编写了一个演练

于 2013-10-04T11:50:18.893 回答
1

最简单的解决方案可能是分离出一个 forCreateUploadID和另一个 for的调用UploadIDData。这假设您可以控制客户端,您可以明确地说只有在 ID 检查后才会上传数据。

如果您想在一个请求中完成所有操作(在解析请求之前检查 ID),您可能必须AuthenticateRequest在 Application 类或 HttpModule 中挂钩应用程序事件(我推荐该模块)。您可以访问此页面以获取对模块可用事件的描述,并在AuthenticateRequest其下方显示

当安全模块需要在处理请求之前对用户进行身份验证时调用此事件。

不幸的是,它可能不起作用,因为“处理请求”可能意味着在 IIS 将请求写入磁盘之后 .NET 处理请求,或者 .NET 将请求的 POST 内容写入磁盘。这取决于写作发生的时间,就像您问的那样,并且IHttpModule将为您提供 .NET 允许的最低级别的抽象。如果它不起作用,您将不得不使用本机或其他平台来完成您想要的。

于 2013-10-03T21:28:06.947 回答