skip to Main Content

I have a React client and a Node server. When I send a POST request to the server, server should zip the folder and download it to the client. This cannot be done. A broken archive is downloaded to the client.
Moreover, the archive on the server is created correctly.

Below code example
React client

function test() {
    fetch("http://localhost:5000/download", {
      method: 'POST'
    })
    .then(res => res.blob())
    .then(data => {
      let url = URL.createObjectURL(data)
      let anchor = document.createElement('a')
      anchor.href = url
      anchor.download = 'ex_new.zip'
      document.body.appendChild(anchor)
      anchor.style = 'display: none'
      anchor.click()
      anchor.remove()

      URL.revokeObjectURL(url)
      document.removeChild(anchor)
    })
}

function App() {
  return (
    <button onClick={() => test()}>Test</button>
  );
}

export default App;

Node server

const archiver = require("archiver")
const express = require("express")
const cors = require("cors")
const fs = require("fs")
const path = require('path')


const app = express()
app.use(cors())

app.post("/download", (req, res) => {

    const archive = archiver('zip')
    archive.directory('output')
    archive.finalize()
    const output = fs.createWriteStream(__dirname + '/ex.zip')
    archive.pipe(output)
    return res.download('/home/user/Js', 'ex.zip')
})

app.listen(5000, () => console.log("Server start"))

Screenshot old error

Screenshot new error

Screenshot Chrome error

Networks Chrome

2

Answers


  1. The problem might be that your node server is not sending the zip file correctly. You are using archiver to make a zip file and write it to a stream, but you are not checking if the stream is done before you send the response. This could make the zip file broken or incomplete. You can fix this by either waiting for the stream to finish with the archive.finalize method, or sending the file right away with the res.attachment method.

    For example:

    app.post("/download", (req, res) => {
      const archive = archiver("zip");
      archive.directory("output");
      archive.finalize();
      res.attachment("ex.zip");
      archive.pipe(res);
    });
    
    Login or Signup to reply.
  2. The problem is that you don’t wait for the file to be created before writing it to the response, which causes the content length specified in the response not to match the actual length of the zip file (and the file content would be incomplete or empty).

    Try like this:

    app.post("/download", (req, res) => {
    
        const archive = archiver('zip')
        archive.directory('output')
        archive.finalize()
        const output = fs.createWriteStream(__dirname + '/ex.zip')
        archive.pipe(output)
        output.on('finish', () => res.download('/home/user/Js', 'ex.zip'))
    })
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search