如何通过 AJAX 返回提示下载的 zip 文件?

Ker*_*rla 5 c# asp.net asp.net-core

我有一个 ASP.NET Core Action 方法,我想返回一个 zip 文件以下载到客户端,它看起来像这样:

[HttpPost, ValidateModel]
public async Task<IActionResult> DownloadCarousel([FromBody]CarouselViewModel model)
{
    File zip = await MediaService.DownloadAndZipCarousel(model.Images, BaseServerPath);
    Response.Headers.Add("Content-Disposition", $"attachment; filename=\"{zip.Name}.zip\"");
    return File(zip.Content, "application/zip", zip.Name);
}
Run Code Online (Sandbox Code Playgroud)

zip.Content 包含我想要作为 zip 返回的字节数组,以便客户端可以下载它。但是,响应不是获取可下载的 zip,而是如下所示:

在此输入图像描述

这是我的服务,如果这有什么关系的话:

public async Task<Models.Models.File> DownloadAndZipCarousel(IEnumerable<Image> images, string baseServerPath)
{
     HttpClient client     = CreateClient();
     var file              = new Models.Models.File();
     var random            = new Random();

     using (var archiveStream = new MemoryStream())
     using (var archive       = new ZipArchive(archiveStream, ZipArchiveMode.Create, true))
     {
          foreach (Image image in images)
          {
               ZipArchiveEntry archiveEntry = archive.CreateEntry($"Instagram{random.Next()}.jpg", CompressionLevel.Optimal);
               using (Stream entryStream = archiveEntry.Open())
               using (Stream contentStream = await client.GetStreamAsync(image.StandartResolutionURL))
               {
                    await contentStream.CopyToAsync(entryStream);
               }
           }

           file.Name    = "RandomZip";
           file.Content = archiveStream.ToArray();
       }

       return file;
}
Run Code Online (Sandbox Code Playgroud)

我没有使用 a MemoryStream,而是尝试使用 aFileStream来测试我的代码是否正常工作并且一切都按预期工作,所以我认为我的操作出了问题,而不是我的服务出了问题。

有人可以告诉我我在这里做错了什么以及我必须做什么才能返回邮政编码吗?

Ker*_*rla 2

在得知我无法提示用户根据请求下载文件这一事实后,POST我对这个问题采取了一种不同的方法,这种方法对我来说非常有效,我想分享我如何解决我的问题。

现在我有2个行动。我的第一个操作在内存中创建 zip 并返回字节数组,之后我将文件保留在缓存中,这样我就不必将其保存在服务器上并稍后检索它,操作如下所示:

[HttpPost, ValidateModel]
public async Task<IActionResult> SaveCarousel([FromBody]CarouselViewModel model)
{
    File zip = await MediaService.ZipCarouselAsync(model.Images, ZipFolder);

    if(zip == null)
    {
        return BadRequest();
    }

    CachingService.Set<File>(zip.Name, zip);
    return Ok(zip);
}
Run Code Online (Sandbox Code Playgroud)

第二个操作响应调用GET并从缓存中获取文件并将其返回以供下载。

[HttpGet]
public IActionResult DownloadCarousel(string name)
{
    File zip = CachingService.Get<File>(name);

    if(zip == null) 
    {
        return BadRequest();
    }

    Response.Headers.Add("Content-Disposition", $"attachment; filename=\"{zip.Name}.zip\"");
    return File(zip.Content, "application/zip");
}
Run Code Online (Sandbox Code Playgroud)

这就是我发出 POST 请求的 AJAX 调用的样子:

$.ajax({
    method: "POST",
    url: "/Media/SaveCarousel",
    data: requestData,
    contentType: "application/json; charset=utf-8",
    success: function (data) {
        window.location = "/Media/DownloadCarousel?name=" + data.name; 
    }, 
    error: function (error) {
        console.log(error);
    }
});
Run Code Online (Sandbox Code Playgroud)

所以基本上我的想法是,通过POST请求发送我需要发送的数据并将 zip 文件保存在缓存上并返回文件名。获得文件名后,我将window.locationAJAX 的 success 方法中的文件名用于从缓存获取文件并将其返回给用户下载的操作。

这样,我不必在服务器上保存任何给定的文件,这意味着我也不必自己维护这些文件,这是我从一开始就试图实现的目标。