我的控制器上有一个动作,应该上传大(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);
}
}
}
答案 0 :(得分:3)
通过实现自定义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);
}
}
写了一篇文章
答案 1 :(得分:1)
最简单的解决方案可能是将CreateUploadID
和UploadIDData
的调用分开。这假设您可以控制客户端,在那里您可以明确地说只有ID检查后才会发送带数据的上传。
如果你想在一个请求中完成所有操作(在解析请求之前检查ID),你可能必须在应用程序类或HttpModule中挂钩应用程序事件AuthenticateRequest
(我推荐该模块)。您可以访问this page以获取模块可用事件的说明,并在AuthenticateRequest
下显示
当安全模块在处理请求之前需要对用户进行身份验证时,请调用此事件。
不幸的是,它可能无法正常工作,因为“处理请求”可能意味着在IIS或.NET将请求的POST内容写入磁盘后,请求的.NET处理。这取决于写作何时发生,就像你问的那样,IHttpModule
将为你提供与.NET允许的低抽象级别。如果它不起作用,你将不得不使用本机或其他平台来完成你想要的东西。