【问题标题】:Downloading large files in ASP.NET Core 2.1 [duplicate]在 ASP.NET Core 2.1 中下载大文件 [重复]
【发布时间】:2021-03-15 08:22:28
【问题描述】:

我正在开发一个 ASP.NET Core 2.1 API 项目,该项目将由 Angular 应用程序使用,稍后将用于移动应用程序。所需功能之一是压缩和下载文件集合,压缩后的文件会很大(1GB 或更多)。 我现在的代码如下:

  1. Angular 应用请求 API 并期望得到 blob 响应。
  2. 服务器上的 API 创建一个 Zip 文件并使用内存流读取它。
  3. API 使用文件响应返回内存流。
  4. Angular 中订阅下载服务的方法保存 文件。

现在发生的情况是,当我在浏览器中单击下载按钮时,我必须等待下载完成,然后浏览器会显示允许用户保存并选择保存位置的默认弹出窗口。

我想知道我所做的是否正确,并且将来不会导致任何内存问题?

是否有更好的方法可以流畅地传输文件,因此当下载开始时,浏览器会直接显示保存消息并显示默认浏览器进度条?

角码:

组件点击功能:

download(event,id){
    event.stopPropagation();
    event.preventDefault();
    
    this.myservice.Downloadservice(avatar_id).subscribe((res: any) => {
    
      saveAs(res.data, res.filename); 
    });
  }

服务代码:

DownloadAllservice(id): Observable<any> {
    let authToken = localStorage.getItem('auth_token');
    let _options = { headers: new Headers({ 'Authorization': `Bearer ${authToken}`, } ),responseType: ResponseContentType.Blob };
    let formData = new FormData();
    let options ={
      type: "zip",
      id: id
    };
    formData.append('options',JSON.stringify(options));
    
    return this.http.post(this.baseUrl + "/api/Download/", formData, _options)
      .map(response =>{
     
       return {
          'filename': this.getFileNameFromHttpResponse(response),
         'data': response.blob()
      } })
      .catch(this.handleError);
  }

.net核心代码:

[Authorize(Policy = "Admin")]
        [DisableRequestSizeLimit]
        [HttpPost("Download", Name = "Download")]
        public async Task<IActionResult> Download()
        {
            // get files list then start creating the temp folder and zipped folder



            var archive = Path.Combine(Directory.GetDirectoryRoot("wwwroot"), @"home\" + FilePath + @"\temp\" + "filename");
            var temp = Path.Combine(Directory.GetDirectoryRoot("wwwroot"), @"home\" + FilePath + @"\temp");
            if (!System.IO.Directory.Exists(temp))
            {
                Directory.CreateDirectory(temp);
            }
           
               
                Directory.CreateDirectory(archive);

            try
            {
               
                foreach (var file_id in filelist)
                {
                   
                    var path = file.path;
                    if (!System.IO.File.Exists(Path.Combine(archive, Path.GetFileName(path)))) {
                        System.IO.File.Copy(path, Path.Combine(archive, Path.GetFileName(path)));
                    }
                    

                }
              
                var archivezip = Path.Combine(Directory.GetDirectoryRoot("wwwroot"), @"home\" + FilePath + @"\temp\" + "filename" + ".zip");

                // create a new archive
                ZipFile.CreateFromDirectory(archive, archivezip);
               
                var memory = new MemoryStream();

                using (var stream = new FileStream(archivezip, FileMode.Open))
                {
                    await stream.CopyToAsync(memory);
                }
                memory.Position = 0;
                Directory.EnumerateFiles(archive).ToList().ForEach(f => System.IO.File.Delete(f));
                Directory.EnumerateDirectories(archive).ToList().ForEach(f => System.IO.Directory.Delete(f, true));
                Directory.Delete(archive);
                System.IO.File.Delete(archivezip);
                return File(memory, "application/octet-stream","filename.zip");

            }
            catch (Exception ex)
            {

                return new BadRequestObjectResult(ex.Message);
            }
        }

请注意,未来不仅 Angular 应用会使用 API,还会添加移动应用

【问题讨论】:

  • 已经导致内存问题 - 它占用了 1GB 的 RAM 来保存可以从磁盘文件中读取的数据。您应该直接返回 ZIP 文件,并在返回后寻找将其删除的方法。这可以像inheriting from one of the FileResult classes 和覆盖ExecuteExecuteAsync 一样简单,以便在下载完成后删除源文件
  • 我不敢相信我已经回答了这样一个问题并忘记了它
  • 谢谢@PanagiotisKanavos,是的,这是我浪费内存的担忧之一

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


【解决方案1】:

我做了类似的事情(我想每个人都这样做,因为这显然是 Angular 的做法)。我只是做了一个基本的无限加载微调器,因为到目前为止,我不需要担心跟踪进度。

但是,有各种guides 可以处理此问题,供您参考。

似乎可以归结为将您的请求从简单的标准 get/post 请求更改为侦听响应事件的请求。

来自链接的文章:

this.http.get(url, {
  reportProgress: true,
  observe: 'events',
  responseType: 'blob'
})

确保它需要进度报告和观察事件的重要部分。一旦请求正在观察事件,那么您需要处理观察它们(显然)。

然而,这是文章中涉及的更长的部分。去吧,祝你好运。

编辑:啊,问题实际上是您担心的 API 方面。很公平,有一个 varietysimilar questions 可能会派上用场。

【讨论】:

  • 问题出在服务器端代码,而不是客户端。服务器代码在 RAM 中加载一个 1GB 的文件,而不是返回存储的文件
  • 啊,明白了,添加了一些指向类似问题的链接,然后在我的回答中。
  • 感谢 @Krenom 和 Panagiotis Kanavos 为我指出这些答案
猜你喜欢
  • 2019-03-12
  • 2019-02-10
  • 2022-02-01
  • 1970-01-01
  • 2015-02-18
  • 2019-10-26
  • 1970-01-01
  • 1970-01-01
  • 2018-07-14
相关资源
最近更新 更多