如何从 dotnet 核心 webapi 下载 Zip 文件?
How to download a ZipFile from a dotnet core webapi?
我正在尝试从 dotnet 核心网络 api 操作下载一个 zip 文件,但我无法让它工作。我尝试通过 POSTMAN 和我的 Aurelia Http Fetch Client 调用操作。
我可以创建我想要的 ZipFile 并将其存储在系统中,但无法修复它,因此它 returns 通过 api 压缩文件。
Use-case:用户选择几张图片collection并点击下载按钮。图片 collections 的 ID 被发送到 api 并创建一个 zip 文件,其中包含每个图片 collection 的目录,其中包含图片。该 zip 文件返回给用户,因此 he/she 可以将其存储在他们的系统上。
如有任何帮助,我们将不胜感激。
我的控制器动作
/// <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 获取客户端函数:
/**
* 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 所做的那样)。当我通过邮递员或应用程序调用它时,根本看不到 headers 被使用。
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)
什么都不做。
HttpContext.Response.ContentType = "application/zip";
var result = new FileContentResult(System.IO.File.ReadAllBytes(zipFile), "application/zip")
{
FileDownloadName = Path.GetFileName(zipFile)
};
return result;
我已经用一个测试虚拟 PDF 文件试过了,它似乎适用于 POSTMAN。但是当我尝试将其更改为压缩文件(见上文)时,它什么也没做。
HttpContext.Response.ContentType = "application/pdf";
var result = new FileContentResult(System.IO.File.ReadAllBytes("THE PATH/test.pdf"), "application/pdf")
{
FileDownloadName = "test.pdf"
};
return result;
长话短说,下面的示例说明了如何通过 dotnet-core 轻松提供 PDF 和 ZIP api:
/// <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;
}
这个示例很管用™
请注意,在这种情况下,这两个操作之间只有一个主要区别(当然除了源文件名之外):返回的 contentType。
上面的示例使用了 'application/zip',正如您自己提到的,但它可能只需要提供不同的 mimetype(如 'application/octet*')。
这导致人们猜测无法正确读取 zip 文件,或者您的网络服务器配置可能没有正确配置以提供 .zip 文件。
后者可能会有所不同,具体取决于您是 运行 IIS Express、IIS、kestrel 等。但是要对此进行测试,您可以尝试将 zip 文件添加到您的 ~/wwwroot 文件夹中,使确保您已在 Status.cs 中启用静态文件服务,以查看是否可以直接下载文件。
我正在尝试从 dotnet 核心网络 api 操作下载一个 zip 文件,但我无法让它工作。我尝试通过 POSTMAN 和我的 Aurelia Http Fetch Client 调用操作。
我可以创建我想要的 ZipFile 并将其存储在系统中,但无法修复它,因此它 returns 通过 api 压缩文件。
Use-case:用户选择几张图片collection并点击下载按钮。图片 collections 的 ID 被发送到 api 并创建一个 zip 文件,其中包含每个图片 collection 的目录,其中包含图片。该 zip 文件返回给用户,因此 he/she 可以将其存储在他们的系统上。
如有任何帮助,我们将不胜感激。
我的控制器动作
/// <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 获取客户端函数:
/**
* 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 所做的那样)。当我通过邮递员或应用程序调用它时,根本看不到 headers 被使用。
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)
什么都不做。
HttpContext.Response.ContentType = "application/zip";
var result = new FileContentResult(System.IO.File.ReadAllBytes(zipFile), "application/zip")
{
FileDownloadName = Path.GetFileName(zipFile)
};
return result;
我已经用一个测试虚拟 PDF 文件试过了,它似乎适用于 POSTMAN。但是当我尝试将其更改为压缩文件(见上文)时,它什么也没做。
HttpContext.Response.ContentType = "application/pdf";
var result = new FileContentResult(System.IO.File.ReadAllBytes("THE PATH/test.pdf"), "application/pdf")
{
FileDownloadName = "test.pdf"
};
return result;
长话短说,下面的示例说明了如何通过 dotnet-core 轻松提供 PDF 和 ZIP api:
/// <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;
}
这个示例很管用™
请注意,在这种情况下,这两个操作之间只有一个主要区别(当然除了源文件名之外):返回的 contentType。
上面的示例使用了 'application/zip',正如您自己提到的,但它可能只需要提供不同的 mimetype(如 'application/octet*')。
这导致人们猜测无法正确读取 zip 文件,或者您的网络服务器配置可能没有正确配置以提供 .zip 文件。
后者可能会有所不同,具体取决于您是 运行 IIS Express、IIS、kestrel 等。但是要对此进行测试,您可以尝试将 zip 文件添加到您的 ~/wwwroot 文件夹中,使确保您已在 Status.cs 中启用静态文件服务,以查看是否可以直接下载文件。