从WebAPI下载Zip存档以响应POST请求

时间:2018-04-10 15:37:06

标签: c# asp.net-web-api download zip axios

我正在尝试提供一个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方法,然后客户端会显示保存对话框?我错过了什么?

1 个答案:

答案 0 :(得分:0)

我设法通过使用...

从控制器操作返回zip来解决这个问题
return File(stream,
            "application/zip",
            "FILENAME.zip");

在客户端代码中,我可以使用this SO answer中的一些JavaScript从标头中获取文件名。

let blob = new Blob([response.data], { type: extractContentType(response) }),
                downloadUrl = window.URL.createObjectURL(blob),
                filename = "",
                disposition = response.headers["content-disposition"];

if (disposition && disposition.indexOf("attachment") !== -1) {
    let filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/,
        matches = filenameRegex.exec(disposition);

    if (matches != null && matches[1]) {
        filename = matches[1].replace(/['"]/g, '');
    }
}

var a = document.createElement("a");
// safari doesn't support this yet
if (typeof a.download === 'undefined') {
    window.location.href = downloadUrl;
} else {
    a.href = downloadUrl;
    a.download = filename;
    document.body.appendChild(a);
    a.click();
}