You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ReactJS调用NodeJS后端下载ZIP文件无法打开问题排查

React客户端无法正确处理NodeJS后端生成的ZIP文件下载

我尝试通过ReactJS客户端下载NodeJS后端生成的ZIP文件,下载得到的文件无法被压缩软件打开,但后端本地保存的调试ZIP文件完全正常。

React客户端代码

{this.state.selectedRows.length > 0 &&
  <>
    <Tooltip title="Baixar arquivos" arrow>
      <IconButton
        onClick={() => {
          const dcs = (!!this.state.selectedRows && this.state.selectedRows.length === 0) ? this.state.fls.map(f => f.doc_id ) : this.state.selectedRows
          this.props.api.post('/mediateca/downloadzipfile', { docs: dcs }).then(r => {
            this.setState({ selectedRows: [] })
            var blob = new Blob([r], { type: "application/octet-stream" });
            saveAs(blob, "example.zip")
          })
        }}>
        <FileDownload />
      </IconButton>
    </Tooltip>
    <D size="0.5rem" />
  </>
}

打印POST响应时,看到类似PK<0x03><0x04><0x14>...的字符串,这是ZIP文件的ASCII序列,但下载的文件大小为238.2KB(后端本地文件为130.8KB),且无法打开。

NodeJS后端代码

async downloadZIPfile(ctx) {
  let u = await ctx.auth.getUser()
  Log.create(!!u ? u.email : "null", ctx.request.url())
  const { docs } = ctx.request.all()
  const rdocs = !!docs && docs.length > 0 ? docs : []
  //Recupera os pdfs do banco de dados e salva um zip no /tmp
  if(rdocs.length > 0){
       var zip = new AdmZip()
       for(let x in rdocs){
           const doc = await Documentos.findOne({ where: { id: rdocs[x] } })
           if (!!doc) {
               const file = await Arquivos.findOne({ where: { id: doc.arquivo_id } })
               var filePath = "/tmp/"+rdocs[x]+".pdf"
               zip.addFile(rdocs[x]+".pdf",file.binario)
           }
       }
       var data = zip.toBuffer()
       var fileName = "flavio.zip"
       zip.writeZip("/tmp/"+fileName)
       ctx.response.header('Content-Type', 'application/octet-stream')
       ctx.response.header('Content-Disposition', `attachment; filename=${fileName}`)
       ctx.response.header('Content-Length', data.length)
       ctx.response.send(data);
  }
  else {
      ctx.response.status(404).send()
  }
  return { status: 'ok' }
}

问题原因

前端请求默认将响应解析为字符串/JSON格式,二进制ZIP数据被转换成UTF-8字符串后,原始二进制结构被破坏。再将这个字符串包装成Blob时,会引入额外的编码字节,导致文件损坏、体积变大。

解决方案

修改请求配置,指定响应类型为blob,让浏览器直接以二进制形式接收数据:

{this.state.selectedRows.length > 0 &&
  <>
    <Tooltip title="Baixar arquivos" arrow>
      <IconButton
        onClick={() => {
          const dcs = (!!this.state.selectedRows && this.state.selectedRows.length === 0) ? this.state.fls.map(f => f.doc_id ) : this.state.selectedRows
          // 添加responseType: 'blob'配置
          this.props.api.post('/mediateca/downloadzipfile', { docs: dcs }, { responseType: 'blob' })
            .then(r => {
              this.setState({ selectedRows: [] })
              // 直接使用响应返回的Blob对象
              saveAs(r, "example.zip")
            })
        }}>
        <FileDownload />
      </IconButton>
    </Tooltip>
    <D size="0.5rem" />
  </>
}

补充说明

如果你的this.props.api是封装后的请求工具(比如axios),确保它支持传递responseType配置。如果底层是原生Fetch API,需要设置response.blob()来处理:

fetch('/mediateca/downloadzipfile', {
  method: 'POST',
  body: JSON.stringify({ docs: dcs }),
  headers: {
    'Content-Type': 'application/json'
  }
})
.then(response => response.blob())
.then(blob => {
  saveAs(blob, "example.zip")
})

内容的提问来源于stack exchange,提问作者Flavio Luis de Mello

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 03:32:14