【发布时间】:2018-04-11 17:32:04
【问题描述】:
我正在尝试提供一个 zip 存档以响应从 Axios 向 WebAPI 发出的 AJAX POST 请求。
在客户端我有
import AjaxDownload from "../../data/AjaxDownload";
AjaxDownload.post(id, pageRecords, {
responseType: "blob"
}).then((response) => {
let blob = new Blob([response.data], { type: extractContentType(response) }),
url = window.URL.createObjectURL(blob);
window.open(url, "_self");
}).catch((error) => {
// ...
}).then(() => {
// ...
});
function extractContentType(response: AxiosResponse): string {
return response.headers["content-type"] || "";
}
// AjaxDownload:
import * as axios from "axios";
import { apiUrl } from "./Ajax";
const ajax = axios.default.create({
baseURL: new URL("download", apiUrl).toString(),
timeout: 3600000 // 1 hour
});
export default ajax;
发布到以下 WebAPI 方法 - 并且该客户端逻辑的 POST 部分完全按预期工作。
[HttpPost]
[Route("download/{id:guid}")]
public async Task<HttpResponseMessage> Download(Guid id, [FromBody] IEnumerable<PageRecord> pageRecords)
{
var stream = await _repo.GetAsArchiveStream(id,
pageRecords,
true).ConfigureAwait(false);
stream.Position = 0;
var result = new HttpResponseMessage(HttpStatusCode.OK) {Content = new StreamContent(stream)};
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment") {FileName = $"{...}.zip"};
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream"); // "application/zip" has same result
result.Content.Headers.ContentLength = stream.Length;
return result;
}
但是,浏览器将 result.Content 显示为 JSON 对象,没有 zip 存档。我假设它显示为 JSON,因为请求提到了 JSON,但为什么它似乎忽略了二进制内容 - 特别是 Content-Type 标头详细说明了内容的类型?
如您所见,JavaScript 也希望将内容作为 blob 读取。
我看不出我的代码与 this answer 有何显着不同 - 请说明是否存在关键区别。
在服务器端,我也尝试过返回...
return new FileStreamResult(stream, "application/zip");
这种方法的问题是无法设置文件名。 Firefox 确实下载了 zip,尽管名称是随机的,而 Chrome 似乎根本没有下载任何东西。
一定有办法做到这一点,对吧?将请求发布到返回 zip 存档的 WebAPI 方法,然后客户端会显示 Save 对话框?我错过了什么?
【问题讨论】:
-
为此使用 POST 很奇怪。如果文件很大怎么办?您现在正在将其加载到浏览器内存中。也许更好地使用常规 GET 请求?
-
我必须使用 POST,因为客户端定义了不适合 GET 查询字符串的文件和结构(可能来自 very 长列表)。至于花费时间,这不是什么大问题,因为 UI 向用户解释并更新用户的方式。
-
我仍然认为从您的 POST 请求中将 url 返回到真实文件是更好的解决方案。现在您使用的是
Blob,您将response.data提供给它,这意味着1)用户只有在从服务器完全下载后才能下载它,并且2)整个文件都在浏览器内存中。如果您对此感到满意,并且您的文件不能大于 500 MB - 那么,这应该可以。但是,如果您尝试以这种方式下载 1GB 之类的内容 - 我相信浏览器只会崩溃。
标签: c# asp.net-web-api download zip axios