如何从dotnet core webapi下载Zip文件?

6
我正在尝试从dotnet core web api操作中下载一个zip文件,但我无法使其工作。我尝试通过POSTMAN和我的Aurelia Http Fetch Client调用该操作。
我能够按照自己的意愿创建ZipFile并将其存储在系统上,但无法修复它以便通过api返回zipfile。
用例:用户选择几个图片集并单击下载按钮。图片集的ID被发送到api,将创建包含每个图片集的目录的zipfile。该zipfile被返回给用户,以便他/她可以将其存储在其系统上。
任何帮助将不胜感激。
我的控制器操作:
      /// <summary>
      /// Downloads a collection of picture collections and their pictures
      /// </summary>
      /// <param name="ids">The ids of the collections to download</param>
      /// <returns></returns>
      [HttpPost("download")]
      [ProducesResponseType(typeof(void), (int) HttpStatusCode.OK)]
      public async Task<IActionResult> Download([FromBody] IEnumerable<int> ids)
      {
           // Create new zipfile
           var zipFile = $"{_ApiSettings.Pictures.AbsolutePath}/collections_download_{Guid.NewGuid().ToString("N").Substring(0,5)}.zip";

           using (var repo = new PictureCollectionsRepository())
           using (var picturesRepo = new PicturesRepository())
           using (var archive = ZipFile.Open(zipFile, ZipArchiveMode.Create))
           {
                foreach (var id in ids)
                {
                     // Fetch collection and pictures
                     var collection = await repo.Get(id);
                     var pictures = await picturesRepo
                          .GetAll()
                          .Where(x => x.CollectionId == collection.Id)
                          .ToListAsync();

                     // Create collection directory IMPORTANT: the trailing slash
                     var directory = $"{collection.Number}_{collection.Name}_{collection.Date:yyyy-MM-dd}/";
                     archive.CreateEntry(directory);

                     // Add the pictures to the current collection directory
                     pictures.ForEach(x => archive.CreateEntryFromFile(x.FilePath, $"{directory}/{x.FileName}"));
                }
           }

           // What to do here so it returns the just created zip file?
      }
 }

我的 Aurelia Fetch 客户端函数:

/**
 * Downloads all pictures from the picture collections in the ids array
 * @params ids The ids of the picture collections to download
 */
download(ids: Array<number>): Promise<any> {
    return this.http.fetch(AppConfiguration.baseUrl + this.controller + 'download', {
        method: 'POST',
        body: json(ids)
    })
}

我尝试过什么

请注意,我所尝试的并没有生成错误,只是似乎没有做任何事情。

1)创建自己的FileResult(就像我以前在旧版ASP.NET中所做的那样)。当我通过Postman或应用程序调用它时,无法看到使用的标头。

return new FileResult(zipFile, Path.GetFileName(zipFile), "application/zip");

 public class FileResult : IActionResult
 {
      private readonly string _filePath;
      private readonly string _contentType;
      private readonly string _fileName;

      public FileResult(string filePath, string fileName = "", string contentType = null)
      {
           if (filePath == null) throw new ArgumentNullException(nameof(filePath));

           _filePath = filePath;
           _contentType = contentType;
           _fileName = fileName;
      }

      public Task ExecuteResultAsync(ActionContext context)
      {
           var response = new HttpResponseMessage(HttpStatusCode.OK)
           {
                Content = new ByteArrayContent(System.IO.File.ReadAllBytes(_filePath))
           };

           if (!string.IsNullOrEmpty(_fileName))
                response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
                {
                     FileName = _fileName
                };

           response.Content.Headers.ContentType = new MediaTypeHeaderValue(_contentType);

           return Task.FromResult(response);
      }
 }

2) https://dev59.com/mlsW5IYBdhLWcg3winvt#34857134

什么也不做。

      HttpContext.Response.ContentType = "application/zip";
           var result = new FileContentResult(System.IO.File.ReadAllBytes(zipFile), "application/zip")
           {
                FileDownloadName = Path.GetFileName(zipFile)
           };
           return result;

我已经尝试了一个测试的虚拟PDF文件,并且在POSTMAN中似乎可以工作。但是,当我尝试将其更改为zipfile(请参见上文),它什么也不做。
  HttpContext.Response.ContentType = "application/pdf";
           var result = new FileContentResult(System.IO.File.ReadAllBytes("THE PATH/test.pdf"), "application/pdf")
           {
                FileDownloadName = "test.pdf"
           };

           return result;
1个回答

17

简而言之,下面的示例演示了如何通过dotnet-core api轻松地同时提供PDF和ZIP服务:

/// <summary>
/// Serves a file as PDF.
/// </summary>
[HttpGet, Route("{filename}/pdf", Name = "GetPdfFile")]
public IActionResult GetPdfFile(string filename)
{
    const string contentType = "application/pdf";
    HttpContext.Response.ContentType = contentType;
    var result = new FileContentResult(System.IO.File.ReadAllBytes(@"{path_to_files}\file.pdf"), contentType)
    {
        FileDownloadName = $"{filename}.pdf"
    };

    return result;
}

/// <summary>
/// Serves a file as ZIP.
/// </summary>
[HttpGet, Route("{filename}/zip", Name = "GetZipFile")]
public IActionResult GetZipFile(string filename)
{
    const string contentType ="application/zip";
    HttpContext.Response.ContentType = contentType;
    var result = new FileContentResult(System.IO.File.ReadAllBytes(@"{path_to_files}\file.zip"), contentType)
    {
        FileDownloadName = $"{filename}.zip"
    };

    return result;
}

这个样例"just works"™

注意,在这种情况下,两个操作仅有一个主要区别(当然,除了源文件名):返回的contentType。

上面的示例使用'application/zip',就像你自己提到的那样,但可能需要使用不同的MIME类型(如'application/octet*')。

这导致人们猜测,要么zip文件无法正确读取,要么您的Web服务器配置可能未正确配置用于提供.zip文件的服务。

后者可能因您是运行IIS Express、IIS、kestrel等而异。但为了测试,您可以尝试将zip文件添加到~/wwwroot文件夹中,并确保在Status.cs中启用了静态文件服务,以查看是否可以直接下载文件。


如果文件很大,由于字节数组的缘故,它将消耗大量内存。有没有一种方法可以在不缓冲的情况下流式传输内容? - bN_

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接