简体   繁体   English

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

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

I am doing an angular application where i have to hit a rest end point and download the file that is sent as the response but i am not understanding how to go about this.我正在做一个有角度的应用程序,我必须到达休息终点并下载作为响应发送的文件,但我不明白如何去做。 I have the response header like the following我有如下响应标头

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

and the response looks like响应看起来像

--Boundary_25_1816124633_1519993185650 Content-Type: application/json --Boundary_25_1816124633_1519993185650 内容类型:application/json

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

EDIT编辑

this is my http call thats ging to the backend这是我到后端的 http 调用

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

How to download the zip file that is being attached?如何下载附加的 zip 文件? Please help.请帮忙。 I am stuck.我卡住了。

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);

We need to set the expected response type, which we'd want to be 'arraybuffer'.我们需要设置预期的响应类型,我们希望它是“arraybuffer”。 Then, we do the usual stuff for FileSaver, that is creating a blob and passing it to the package's saveAs function.然后,我们为 FileSaver 做通常的事情,即创建一个 blob 并将其传递给包的saveAs函数。

Edit编辑

As per the comment, a clarification was asked for parsing the various parts of the multipart response.根据评论,要求澄清解析多部分响应的各个部分。

Info about the Multipart content type有关多部分内容类型的信息

The boundary, as defined in the header, indicates the different parts of the response.标头中定义的边界指示响应的不同部分。 Each of the parts are preceded by the line每个部分前面都有一行

--boundaryThatIsInTheHeader

Method方法

We can split the response as per the boundary.我们可以根据边界拆分响应。 To do this, we must first parse the boundary from the header.为此,我们必须首先从标头中解析出边界。 RegEx to our rescue here: 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

Now, we need to split the response with the boundary as the delimiter.现在,我们需要以边界作为分隔符来拆分响应。

response.split(boundary);

The value returned for this specific response of the server would be为服务器的这个特定响应返回的值将是

[ "", " 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³Ù,È«ù"]

Notice the second element of the array, that's the JSON.请注意数组的第二个元素,即 JSON。 That's what we desired, We can now remove the extra data.这就是我们想要的,我们现在可以删除额外的数据。 ie,, content type.即,内容类型。 by using simple RegEx, If the format of the response is constant.通过使用简单的正则表达式,如果响应的格式是不变的。 we can also directly remove it as per the index.我们也可以直接根据索引删除它。 Same is the case for the zip file's content. zip 文件的内容也是如此。

I believe multipart/mixed and multipart/form-data share some common structure.我相信multipart/mixedmultipart/form-data共享一些共同的结构。
(not exactly sure what the difference is.) (不确定有什么区别。)

form-data is mostly used for sending forms to the server but could as well be used the other way around. form-data主要用于向服务器发送表单,但也可以反过来使用。

the fetch api has a method called .formData() fetch api 有一个名为.formData()的方法

This is mainly relevant to service workers.这主要与服务人员有关。 If a user submits a form and a service worker intercepts the request, you could for example call formData() on it to obtain a key-value map, modify some fields, then send the form onwards to the server (or use it locally).如果用户提交了一个表单并且一个 service worker 拦截了请求,你可以调用它的 formData() 来获得一个键值映射,修改一些字段,然后将表单发送到服务器(或在本地使用它) .

So if we can get the response and change the content-type header to multipart/form-data we could utilize the fetch api to read the content without having to parse it.因此,如果我们可以获得响应并将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"}}))})};

Yes, I've been looking for this for so long.是的,我一直在寻找这个。 probably should have figured it out sooner.可能应该早点弄明白。 thought I'd add it the thread.以为我会添加它的线程。

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);

My response included a JSON part and Zip part which can then be saved to disk.我的回复包括一个 JSON 部分和 Zip 部分,然后可以将其保存到磁盘。

Thanks!谢谢!

You can use file-saver package to make it clearer in code.您可以使用文件保护程序包使其在代码中更清晰。 Then service method for fetching data from back-end:然后是从后端获取数据的服务方法:

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

Then call function on browers event然后在浏览器事件上调用函数

 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