nodejs转发服务端请求并返回二进制流 前端导出文件

4,081 阅读1分钟

因前几天碰到一个node转发Java请求并返回二进制流给前端,前端取不到服务端返回的header头 Content-Disposition 文件名。在做这个内容的时候,由于接触node的时间过短,走了很多弯路,查了好多,总算是搞出来了。特此写一篇文章来纪念一下。

node核心代码:

 async download() {
    const ctx = this.ctx;
    const result = await ctx.curl(api.download, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      data: {
        ...ctx.request.body,
      },
    });
    ctx.set({
      'Access-Control-Expose-Headers': 'Content-Disposition',//核心配置
      'Content-Type': 'application/octet-stream',//核心配置
      ...result.headers,
    });
    ctx.body = result.data;
  }

前端之所以一直取不到header头返回的Content-Disposition是因为没有配置'Access-Control-Expose-Headers'。重要的事情标注了,不讲三遍啦!

前端代码:

使用react+umi+dva实现:

server.js

export async function download(params) {
  return fetch('/download', {
    method: 'POST',
    credentials: 'include',
    headers: {
      'content-type': 'application/json',
    },
    body: JSON.stringify(params),
  }).then(res => {
    return res.blob().then(blob =>
      Promise.resolve({
        blob,
        res,
      }),
    );
  });
}

model.js

 import { saga } from 'dva';
 const { call, select, put } = saga.effects; 
 const ModelPage = {
    namespace: 'history',
    effects: {
      *download({ payload }) {
        const { blob, res } = yield call(download, payload);
        let blobUrl = window.URL.createObjectURL(blob);
        //console.log(res.headers.get('content-disposition'));
        let filename = res.headers.get('content-disposition').split('filename=')[1];
        filename = filename.substring(1, filename.length - 1);
        const tagA = document.createElement('a');
        tagA.href = blobUrl;
        tagA.download = filename;
        tagA.click();
        // window.URL.revokeObjectURL(blobUrl);
      },
    }
 }
 export default ModelPage;

结论

1. 无论下载什么链接,异步请求都无法直接下载,必须通过a标签或打开新窗口进行下载

(1)a标签设置download属性,可以直接下载普通链接能够访问的文件

(2)响应头设置'content-type': 'application/json'

2. node转发服务端将文件流返回给前端的时候,要设置请求头

'Access-Control-Expose-Headers': 'Content-Disposition',//核心配置
'Content-Type': 'application/octet-stream',//核心配置

3.在返回文件流的时服务端一定要在请求头带上文件流的字节长度(Content-Length)!!切记!切记!切记!

否则,前端要么下载文件失败,要么下载下来的文件无法打开。