Create and Send Zip file -NODE JS
Asked Answered
D

5

10

I'm trying to create and then send zip file to client. I know how to create it but I've got a problem with send it to client. I tried many ways. I'm sending POST request from Client and as response I want to send a file. This is my server-site example code

var Zip = require('node-zip');
router.post('/generator', function(req, res, next) {
    var zip = new Zip;

    zip.file('hello.txt', 'Hello, World!');
    var options = {base64: false, compression:'DEFLATE'};
    fs.writeFile('test1.zip', zip.generate(options), 'binary', function (error) {
        console.log('wrote test1.zip', error);
    });
    res.setHeader('Content-disposition', 'attachment; filename=test1.zip');
    res.download('test1.zip');

}

}); I also tried something like this:

  res.setHeader('Content-disposition', 'attachment; filename=' + filename);
  res.setHeader('Content-type', mimetype);

  var filestream = fs.createReadStream(file);
  filestream.pipe(res);

I tried to use such libraries as:

  1. node-zip

  2. archiver

Can anyone explain me how to do that ?

Duotone answered 21/10, 2015 at 13:34 Comment(1)
Hi @PtDf, did you got the solution? I am also stuck in such situation. I am not able to download the zip file. Please let me know if you solved it. ThanksCataplexy
D
8

This module works fine too: https://www.npmjs.com/package/adm-zip

Example without creating temporary zip file in server:

var AdmZip = require('adm-zip');
router.get('/zipFilesAndSend', function(req, res) {
    var zip = new AdmZip();
    // add local file
    zip.addLocalFile("./uploads/29/0046.xml");
    // get everything as a buffer
    var zipFileContents = zip.toBuffer();
    const fileName = 'uploads.zip';
    const fileType = 'application/zip';
    res.writeHead(200, {
        'Content-Disposition': `attachment; filename="${fileName}"`,
        'Content-Type': fileType,
      })
    return res.end(zipFileContents);
});
Dram answered 7/10, 2020 at 10:23 Comment(5)
This is the better solution for me. Ideal if you don't want a sub-folder which you will get with express-easy-zipCatechetical
Hello, It's better for me too this solution, but what about the front to donwload this ? thanksLolly
@Lolly you just browse to the '/zipFilesAndSend' path and the file will be downloaded at the browserDram
@Dram , my case is on post and not get :). I use something like this : .then(response => response.arrayBuffer()) and try to download the response bufferLolly
@Dram I got the solution simply using the downloadjs , thanksLolly
B
5

I haven't worked with node-zip or archiver before (I usually just use the built-in zlib module), but one thing I noticed right away is that you should place res.download inside the callback of writeFile. That way it will only send the file once it has been fully written to disk.

fs.writeFile('test1.zip', zip.generate(options), 'binary', function (error) {
    res.download('test1.zip');
});

I hope this solution works for you, if it doesn't feel free to comment.

Also, I think res.download sets the Content-disposition header for you, you don't need to set it manually. Not 100% sure on that one though.

Bastardize answered 21/10, 2015 at 13:53 Comment(6)
It dosent work. I did as you wrote. But as response i got some dumb text in browser console output.Duotone
Did you check the file test1.zip to see if it actually contains what you want it to contain?Bastardize
Yes it contains hello.txt. In browser console output i can see hello.txt and some dumb text. I'm trying to do it local, this may have some impact ?Duotone
Hang on a sec, you're downloading a file but you're checking it in the browser console? I'm not sure I follow what you're trying to do. Downloading a file like that is when you want to save the file to your harddisk. If you just want to send data to the browser, you should use res.send instead. Could you perhaps edit your original post and explain a bit what you're trying to achieve, what you want to do with the data you send to the client?Bastardize
Yes i would like to downloading the file on my hard disk after I send by POST some data. I did everything like you wrote but downloading doesn't start. Forgot about the console output it doesn't matter.Duotone
Then to track down the problem, you could log your app and your data every step of the process and see where it goes wrong.Bastardize
M
4

Try this express-easy-zip npm package to generate a zip file from a local folder path and send it as a download to the client.

var zip = require('express-easy-zip');
var app = require('express')();

app.use(zip());

app.get('my-route/zip', async function(req, res) {
    var dirPath = __dirname + "/uploads";
    await res.zip({
        files: [{
            path: dirPath,
            name: 'Package'
        }],
        filename: 'Package.zip'
    });
});
Maxfield answered 26/3, 2019 at 14:54 Comment(1)
Thank you for this code snippet, which might provide some limited, immediate help. A proper explanation would greatly improve its long-term value by showing why this is a good solution to the problem and would make it more useful to future readers with other, similar questions. Please edit your answer to add some explanation, including the assumptions you’ve made.Schenck
G
1

Above solutions work.(above solutions generate zip and send it to frontend as data in response. In order to make it as downloadable following code will work) I was using express-zip. It is compressing files and sending data to frontend from backend(node). But in frontend I was getting only data in response. In my case I want user can be able to download the zip which sent by server. To solve this I followed following approach. For generating download window in browser i used downloadjs (we can follow another approach but i find this easy)

Front-End

const download = require('downloadjs')
return axios({
        url:process.env.API_HOST+'/getuploadedfiles',
        method:'get',
        headers:{
            'Content-Type': 'multipart/form-data',
            withCredentials:true,


        },
        responseType:'arraybuffer' // If we don't mention we can't get data in desired format
    })
    .then(async response => {
        console.log("got al files in api ");
        let blob = await new Blob([response.data], { type: 'application/zip' }) //It is optional

        download(response.data,"attachement.zip","application/zip") //this is third party it will prompt download window in browser.

        return response.data;
    })

Backe-End

const zip = require('express-zip');
app.use('/getuploadedfiles',function(req,res){
     res.zip([
    {path:'/path/to/file/file2.PNG',name:'bond.png'},
    {path:'/path/to/file/file1.PNG',name:'james.png'}
])
Genesis answered 9/5, 2019 at 3:55 Comment(0)
C
0

Hope this helps! This worked for me when I was making a file server recently.

const express                     = require('express')
const path                        = require('path')
const archiver                    = require('archiver')
const { promises: fs, constants } = require("fs")

const app = express()

app.get('*.zip', async (req,res)=>{
  const parsedReqPath        = decodeURIComponent(req.path)
  const withoutZip           = parsedReqPath.replace(/.zip$/,"")
  const currentPathWithZip   = path.join(process.env.PWD, parsedReqPath)
  const currentPathNoZip     = path.join(process.env.PWD, withoutZip)
  const folderName           = currentPathNoZip.replace(process.env.PWD, '')
  const recursiveFlag        = true
  // if the zip exists send the zip
  const zipExists = await fs.access(currentPathWithZip, constants.F_OK).catch((err)=>{return true}) ? false : true
  if (zipExists){
    res.sendFile(currentPathWithZip, {dotfiles:'allow'})
  }
  // else make the zip on the fly and stream it
  else{
    const pathExists = await fs.access(currentPathNoZip, constants.F_OK).catch((err)=>{return true}) ? false : true
    if (pathExists){
      const pathStats = await fs.lstat(currentPathNoZip)
      if(pathStats.isDirectory()){
        const archive = archiver('zip')
        archive.on('error', function(err) { console.log(err);res.send('an error occured on the server') })
        archive.pipe(res)
        archive.directory(currentPathNoZip, folderName, recursiveFlag).finalize()
      }
    }else{
      res.send('path not found...')
    }
  }
})

app.listen(8000, ()=>{
    console.log("Listening on port 8000")
})

This code creates an express http server, accepts requests for any zip file (any path ending in .zip), and if the path exists, reccursivly zips the directory and sends it.

Crispas answered 5/7 at 16:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.