Downloading a file with content type Content-Type:multipart/mixed Downloading a file with content type Content-Type:multipart/mixed angular angular

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


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'. Then, we do the usual stuff for FileSaver, that is creating a blob and passing it to the package's saveAs function.

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:

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 questionconst 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. That's what we desired! We can now remove the extra data, i.e., 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.


I believe multipart/mixed and multipart/form-data share some common structure.
(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.

the fetch api has a method called .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).

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.

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.

Thanks!