ASP.Net Web API:在读取/上载请求主体之前发送响应

时间:2013-10-03 19:50:30

标签: asp.net asp.net-mvc asp.net-web-api

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

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

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

我以为我已经钉了这个,因为如果没有创建uploadId,我几乎可以立即拒绝HTTP请求,抛出一个HttpResponseException,但我刚刚发现,即使在这种情况下,Request的正文得到了完全上传到服务器并保存到磁盘上的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);
                }
            }
        }

2 个答案:

答案 0 :(得分:3)

通过实现自定义WebHostBufferPolicySelector

,您可以将Web API配置为在特定控制器(或全局)的无缓冲模式(流式传输)中工作
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);
   }
}

我已经为dealing large files in Web API

写了一篇文章

答案 1 :(得分:1)

最简单的解决方案可能是将CreateUploadIDUploadIDData的调用分开。这假设您可以控制客户端,在那里您可以明确地说只有ID检查后才会发送带数据的上传。

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

  

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

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