【问题标题】:Can I abort a large FileStreamResult download on server side?我可以在服务器端中止大型 FileStreamResult 下载吗?
【发布时间】:2021-07-23 15:20:26
【问题描述】:

我有一个动作,我只需下载一个文件。有时,如果文件很大,用户希望中止下载而不是等待完成。

private IActionResult Download(string path)
{
    var length = new FileInfo(path).Length;

    Response.Headers.Add("size", length.ToString());

    Stream str = System.IO.File.OpenRead(path);

    return File(str, "application/x-zip-compressed");
}

我想知道的是,我们是否可以在后端中止该进程,也许将某些任务存储在 ram 上以在另一个进程上取消。

为更好地理解而编辑:在我从浏览器订购该设备必须下载文件后,该文件下载到调用下载方法的不同设备中。因此,需要通过从我的浏览器调用操作来在服务器端中止任务。

【问题讨论】:

    标签: asp.net-mvc asp.net-core


    【解决方案1】:

    在服务器端,您可以通过HttpContext.RequestAborted 中止请求时收到通知,这是一个CancellationToken,当请求的底层连接被中止时会被取消。因此,如果它们支持取消,您可以将其传递给您调用的异步方法,或者您可以通过 CancellationToken.Register() 挂钩回调以处理取消逻辑。

    但是,在您的代码示例中,这不是必需的。您所做的只是创建一个FileStream,而不读取文件的内容。文件内容的实际读取由框架执行,当它处理由调用File(str, "application/x-zip-compressed") 创建的FileStreamResult 时。从source codeFileResultExecutorBase可以看出,当HttpContext.RequestAborted被取消时,框架会自动取消文件读取。

    【讨论】:

      【解决方案2】:

      好吧,我终于通过处理 HttpContexts 的全局集合解决了这个问题。当我的下载操作被调用并开始下载时,我存储下载请求 HttpContext。另外,我有一个不同的操作/方法/whatelse,我在其中读取上下文并调用.Abort() 方法。

      private void CancelDownload(string key) //action, method...
      {
          string uniqueKeyDownload = externalDevice.ID + "#" + fileID;
                              
          log.Information("Cancelling download 1/3..." + uniqueKeyDownload );
      
          if (Startup.MyDictionaryContexts.ContainsKey(uniqueKeyDownload ))
          {
              log.Information("Cancelling download 2/3..." + uniqueKeyDownload );
      
              if (!Startup.MyListDownloadsCancelled.Contains(uniqueKeyDownload ))
              {
                  log.Information("Cancelling download 3/3..." + uniqueKeyDownload );
      
                  Startup.MyListDownloadsCancelled.Add(uniqueKeyDownload );
      
                  Startup.MyDictionaryContexts[uniqueKeyDownload ].Abort();
              }
          }
      
      }
      
      
      
      
      public IActionResult Download(string path)
      {
      
          string uniqueKeyDownload = externalDevice.ID + "#" + fileID;
      
          if (!Startup.MyDictionaryContexts.ContainsKey(uniqueKeyDownload ))
          {
              Startup.MyDictionaryContexts.Add(uniqueKeyDownload, HttpContext);
          }
      
          HttpContext.Response.OnCompleted(async () =>
          {
      
               //remove context from dictionary
               if (Startup.MyDictionaryContexts.ContainsKey(uniqueKeyDownload))
                   Startup.MyDictionaryContexts.Remove(uniqueKeyDownload);
      
               //Log if event was fired on completion or abort call
               if (Startup.MyListDownloadsCancelled.Contains(uniqueKeyDownload))
               {
                   Startup.MyListDownloadsCancelled.Remove(uniqueKeyDownload);
      
                   log.Information("DOWNLOAD CANCELLED!!!");
               }
               else
                   log.Information("DOWNLOAD COMPLETED!!!");
      
          });
      
          //if you are downloading on browser and make recalls, manage this block
          //  with conditions and return NoContent() or similar
          
          var length = new FileInfo(path).Length;
      
          Response.Headers.Add("size", length.ToString());
      
          Stream str = System.IO.File.OpenRead(path);
      
          log.Information("DOWNLOAD STARTED..." + uniqueKeyDownload);
      
          return File(str, "application/x-zip-compressed");
      }
      

      我已经意识到,根据下载文件的客户端,会有不同的行为。如果客户端是来自 Java 的 http 客户端(我的情况),客户端将出现套接字异常,仅此而已。但是,如果您在浏览器上下载(使用 Chrome 测试),在调用 .Abort() 之后,浏览器将再请求一次下载操作,在这种情况下,您将不得不管理一些标志 return NoContent() 或类似的return File(..) 再次。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-12-25
        • 1970-01-01
        • 2021-03-14
        • 2021-09-08
        • 1970-01-01
        相关资源
        最近更新 更多