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

时间:2018-03-02 12:24:24

标签: javascript angular

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

  

内容处置:附件;文件名= “Config.zip”   内容类型:多部分/混合;边界= Boundary_25_1816124633_1519993185650   MIME版本:1.0 Transfer-Encoding:chunked

,响应看起来像

  

- Boundary_25_1816124633_1519993185650内容类型:application / json

     

{ “配置”:[{},{},{}]}   --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¬®úªú«A·ö«åºv〜\ I〜ùöýw³Ù,E«ù

修改

这是我的http电话,它指向后端

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

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

4 个答案:

答案 0 :(得分:7)

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

我们需要设置预期的响应类型,我们希望将其作为阵列缓冲区'。然后,我们为FileSaver做了常用的事情,即创建一个blob并将其传递给包的saveAs函数。

修改

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

有关Multipart内容类型的信息

标题中定义的边界表示响应的不同部分。每个部分都以行

开头

--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。这就是我们想要的!我们现在可以使用简单的RegEx删除额外的数据,即内容类型。如果响应的格式是常量,我们也可以根据索引直接删除它。 zip文件的内容也是如此。

答案 1 :(得分:1)

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

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

fetch api有一个名为.formData()

的方法
  

这主要与服务人员有关。如果用户提交表单并且服务工作者截获请求,您可以例如在其上调用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"}}))})};

答案 2 :(得分:0)

是的!我找了这么久,应该早点弄明白的。我想我会把它添加到线程中。

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 部分,然后可以将其保存到磁盘。

谢谢!

答案 3 :(得分:-1)

您可以使用file-saver包来使代码更清晰。然后是从后端获取数据的服务方法:

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