繁体   English   中英

下载内容类型为 Content-Type:multipart/mixed 的文件

[英]Downloading a file with content type Content-Type:multipart/mixed

我正在做一个有角度的应用程序,我必须到达休息终点并下载作为响应发送的文件,但我不明白如何去做。 我有如下响应标头

内容处理:附件; filename="Config.zip" Content-Type:multipart/mixed;boundary=Boundary_25_1816124633_1519993185650 MIME-Version:1.0 Transfer-Encoding:chunked

响应看起来像

--Boundary_25_1816124633_1519993185650 内容类型:application/json

{"config":[{},{},{}]} --Boundary_25_1816124633_1519993185650 内容类型:application/octet-stream

PKMÛJAä;%RecurrenceEvent_CreateContract_1.jsoníYKoãF¾ÈxÝ0è÷Ã7Mb L&íÝK0ͦCD¬1ðß(J¤HÙ²¼yV'»ÙU¬®úªú«â· ö«åºv~\Í~ùöýw³Ù,È«ù

编辑

这是我到后端的 http 调用

return this.http.get(url).map(response => {
// TODO
});

如何下载附加的 zip 文件? 请帮忙。 我卡住了。

const executeSaveAs = (content) => {
    let blob = new Blob([content], {'type': "application/octet-stream"});
    saveAs(blob, "downloaded_zip.zip"); // This is from https://github.com/eligrey/FileSaver.js
};

return this.http.get(url, {responseType: 'arraybuffer'}).pipe(executeSaveAs);

我们需要设置预期的响应类型,我们希望它是“arraybuffer”。 然后,我们为 FileSaver 做通常的事情,即创建一个 blob 并将其传递给包的saveAs函数。

编辑

根据评论,要求澄清解析多部分响应的各个部分。

有关多部分内容类型的信息

标头中定义的边界指示响应的不同部分。 每个部分前面都有一行

--boundaryThatIsInTheHeader

方法

我们可以根据边界拆分响应。 为此,我们必须首先从标头中解析出边界。 RegEx 在这里拯救我们:

let boundaryRegex = new RegExp(/boundary=(\S+)/g);
const header = `Content-Disposition:attachment; filename="Config.zip" Content-Type:multipart/mixed;boundary=Boundary_25_1816124633_1519993185650 MIME-Version:1.0 Transfer-Encoding:chunked`; // As in the question

const boundary = '--' + boundaryRegex.exec(header)[1]; // We obtain the boundary here

现在,我们需要以边界作为分隔符来拆分响应。

response.split(boundary);

为服务器的这个特定响应返回的值将是

[ "", " Content-Type: application/json\n\n {\"config\":[{},{},{}]} ", " Content-Type: application/octet-stream\n\n PKMÛJAä;%RecurrenceEvent_CreateContract_1.jsoníYKoãF¾ÈxÝ0è÷Ã7Mb L&íÝK0ͦCD¬1ðß(J¤HÙ²¼yV'»ÙU¬®úªú«â· ö«åºv~Í~ùöýw³Ù,È«ù"]

请注意数组的第二个元素,即 JSON。 这就是我们想要的,我们现在可以删除额外的数据。 即,内容类型。 通过使用简单的正则表达式,如果响应的格式是不变的。 我们也可以直接根据索引删除它。 zip 文件的内容也是如此。

我相信multipart/mixedmultipart/form-data共享一些共同的结构。
(不确定有什么区别。)

form-data主要用于向服务器发送表单,但也可以反过来使用。

fetch api 有一个名为.formData()的方法

这主要与服务人员有关。 如果用户提交了一个表单并且一个 service worker 拦截了请求,你可以调用它的 formData() 来获得一个键值映射,修改一些字段,然后将表单发送到服务器(或在本地使用它) .

因此,如果我们可以获得响应并将content-type标头更改为multipart/form-data ,我们就可以利用 fetch api 读取内容而无需解析它。

 getExampleResponse(async res => { // replace the content-type to multipart/form-data so fetch api can parse it const type = res.headers.get('content-type') res.headers.set('content-type', type.replace('mixed', 'form-data')) // return the response as a formData const fd = await res.formData() // console.log(...fd) console.log(JSON.parse(fd.get('json'))) console.log(fd.get('image')) const file = fd.get('image') const link = document.createElement('a') const image = new Image link.href = image.src = URL.createObjectURL(file) link.innerText = 'download ' + (link.download = file.name) // saveAs(file); // This is from https://github.com/eligrey/FileSaver.js document.body.appendChild(image) document.body.appendChild(link) }) /* Don't mind this, it's just an example response you are getting... What you actually want is something like function getExampleResponse(cb) { fetch(url).then(cb) } */ function getExampleResponse(e){var a=document.createElement("canvas"),d=a.getContext("2d");d.fillStyle="blue";d.fillRect(0,0,a.width,a.height);a.toBlob(function(b){var a={a:123};var c='--Boundary_25_1816124633_1519993185650\r\nContent-Disposition: form-data; name="json"\r\nContent-Type: application/json\r\n'+("Content-Length: "+JSON.stringify(a).length+"\r\n\r\n");c+=JSON.stringify(a)+"\r\n";c=c+'--Boundary_25_1816124633_1519993185650\r\nContent-Disposition: form-data; name="image"; filename="image.png"\r\nContent-Transfer-Encoding: binary\r\n'+ ("Content-Type: "+b.type+"\r\n");c+="Content-Length: "+b.size+"\r\n\r\n";b=new Blob([c,b,"\r\n--Boundary_25_1816124633_1519993185650--\r\n"]);e(new Response(b,{headers:{"Content-Type":"multipart/mixed; boundary=Boundary_25_1816124633_1519993185650"}}))})};

是的,我一直在寻找这个。 可能应该早点弄明白。 以为我会添加它的线程。

var boundIndex = res.headers['content-type'].indexOf('boundary=') + 'boundary='.length;
var bound = '--' + res.headers['content-type'].slice(boundIndex, res.headers['content-type'].length);

var emanData = res.data.split(bound);

我的回复包括一个 JSON 部分和 Zip 部分,然后可以将其保存到磁盘。

谢谢!

您可以使用文件保护程序包使其在代码中更清晰。 然后是从后端获取数据的服务方法:

getFile(fileName: string): Observable<Blob> {
    return this.http.get(`${environment.apiServerUrl}/` + fileName, {responseType: 'blob'})
    .catch(error => this.handleErrorResponse() // catch http error);
  }

然后在浏览器事件上调用函数

 this.service.getFile('filenameInServer.zip')
     .subscribe(fileData => FileSaver.saveAs(fileData, 'sugestedFileNameInDownloadDialog.zip'));

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM