12

I try to find example where I can send a zip (like via postman) and get this zip in my handler and unzip it so specified folder I didn't find much examples for zipping using express I want to unzip it in path web/app

I try something like the following which doesn't works for me , the zip file is not unzipped in the specified folder, any idea what im doing wrong ?

https://nodejs.org/api/zlib.html#zlib_zlib

var zlib = require('zlib');
var fs = require('fs');
const dir = path.join(__dirname, 'web/app/');

if (req.file.mimetype === 'application/zip') {

    var unzip = zlib.createUnzip();

    var read = fs.createReadStream(req.file);
    var write = fs.createWriteStream(dir);
    //Transform stream which is unzipping the zipped file
    read.pipe(unzip).pipe(write);   
    console.log("unZipped Successfully");

}

Any working example will be very helpful, or reference where can I've problem...

while debug I see the that this is when the code failed

var read = fs.createReadStream(req.file);

any idea why?

I've also tried with

var read = fs.createReadStream(req.file.body);

the issue that I dont see the error, reason etc.

when I change it to

var read = fs.createReadStream(req.file.buffer);

the program doesnt exit and i was able to run it until the logger console.log("unZipped Successfully"); but nothing happen...

if there any example with https://www.npmjs.com/package/yauzl yauzl and multer in my context it will be great

update- this is the postman request

enter image description here

  • Recieving files is always a bit of work. Maybe you could try to save the file and opening it with a gui for debugging purpose? You might get valueble info. One step at the time – Lars C. Magnusson Jul 09 '18 at 20:17
  • This is easy using OS scripts. You didn't specify any restrictions about it, is there any? – Emeeus Jul 17 '18 at 13:55

4 Answers4

8

First of all, zlib does not support extracting zip files.

I recommend formidable for handling files because

  1. its battle tested
  2. most widely used
  3. avoids writing boilerplate plate code like reading filestream from request, storing and handling errors
  4. easily configurable

Prerequisites
Install dependencies using npm i -S extract-zip formidable express or yarn add extract-zip formidable express

Bare minimal solution for your problem with formidable and extract-zip

const express = require('express');
const fs = require('fs');
const extract = require('extract-zip')
const formidable = require('formidable');
const path = require('path');
const uploadDir = path.join(__dirname, '/uploads/');
const extractDir = path.join(__dirname, '/app/');
if (!fs.existsSync(uploadDir)) {
  fs.mkdirSync(uploadDir);
}
if (!fs.existsSync(extractDir)) {
  fs.mkdirSync(extractDir);
}

const server = express();

const uploadMedia = (req, res, next) => {
  const form = new formidable.IncomingForm();
  // file size limit 100MB. change according to your needs
  form.maxFileSize = 100 * 1024 * 1024;
  form.keepExtensions = true;
  form.multiples = true;
  form.uploadDir = uploadDir;

  // collect all form files and fileds and pass to its callback
  form.parse(req, (err, fields, files) => {
    // when form parsing fails throw error
    if (err) return res.status(500).json({ error: err });

    if (Object.keys(files).length === 0) return res.status(400).json({ message: "no files uploaded" });
    
    // Iterate all uploaded files and get their path, extension, final extraction path
    const filesInfo = Object.keys(files).map((key) => {
      const file = files[key];
      const filePath = file.path;
      const fileExt = path.extname(file.name);
      const fileName = path.basename(file.name, fileExt);
      const destDir = path.join(extractDir, fileName);

      return { filePath, fileExt, destDir };
    });

    // Check whether uploaded files are zip files
    const validFiles = filesInfo.every(({ fileExt }) => fileExt === '.zip');

    // if uploaded files are not zip files, return error
    if (!validFiles) return res.status(400).json({ message: "unsupported file type" });

    res.status(200).json({ uploaded: true });

    // iterate through each file path and extract them
    filesInfo.forEach(({filePath, destDir}) => {
      // create directory with timestamp to prevent overwrite same directory names
      extract(filePath, { dir: `${destDir}_${new Date().getTime()}` }, (err) => {
        if (err) console.error('extraction failed.');
      });
    });
  });

  // runs when new file detected in upload stream
  form.on('fileBegin', function (name, file) {
    // get the file base name `index.css.zip` => `index.html`
    const fileName = path.basename(file.name, path.extname(file.name));
    const fileExt = path.extname(file.name);
    // create files with timestamp to prevent overwrite same file names
    file.path = path.join(uploadDir, `${fileName}_${new Date().getTime()}${fileExt}`);
  });
}

server.post('/upload', uploadMedia);

server.listen(3000, (err) => {
  if (err) throw err;
});

This solution works for single/multiple file uploads. The one problem with this solution is, wrong file types will get uploaded to uploaded directory though server throw error.

To test with postman: postman image

Community
  • 1
  • 1
REDDY PRASAD
  • 1,309
  • 2
  • 14
  • 29
2

Without a full example it's tough to say what the real problem is. But according to Express docs it says:

In Express 4, req.files is no longer available on the req object by default. To access uploaded files on the req.files object, use multipart-handling middleware like busboy, multer, formidable, multiparty, connect-multiparty, or pez.

So if you are not using a middleware library to handle uploading files, it's tough to tell what the value of req.file is.

I am also a bit worried that you are trying to use zlib to decompress a zip file, since the library only supports gzip.

The zlib module provides compression functionality implemented using Gzip and Deflate/Inflate

You would want to check for req.file.mimetype === 'application/gzip'

Here are some posts related to unzipping zip files:

jjbskir
  • 8,474
  • 9
  • 40
  • 53
2

Prerequisites:

  1. npm i express unzipper multiparty bluebird
  2. Create app/web directory in your project root (or you can automate creation if you want).
  3. Place all of these files into one directory.
  4. Node version that supports async/await (7.6+ as far as I know)

server.js:

const express = require('express');
const Promise = require('bluebird');
const fs = require('fs');
const writeFile = Promise.promisify(fs.writeFile);

const { parseRequest, getFile } = require('./multipart');
const { extractFiles } = require('./zip')

const EXTRACT_DIR = 'web/app';

const app = express();

const uploadFile = async (req, res, next) => {
  try {
    const body = await parseRequest(req);
    const bodyFile = getFile(body, 'file');
    if (!/\.zip$/.test(bodyFile.originalFilename)) {
      res.status(200).json({ notice: 'not a zip archive, skipping' })
      return;
    }
    const archiveFiles = await extractFiles(bodyFile);

    await Promise.each(archiveFiles, async (file) => {
      await writeFile(EXTRACT_DIR + '/' + file.path, file.buffer);
    })
    res.status(200).end();
  } catch (e) {
    res.status(500).end();
  }
};

app.post('/files', uploadFile);

app.listen(3000, () => {
  console.log('App is listening on port 3000');
});

multipart.js

const Promise = require('bluebird');
const { Form } = require('multiparty');

function parseRequest (req, options) {
    return new Promise((resolve, reject) => {
        const form = new Form(options)
        form.parse(req, (err, fields, files) => {
            if (err) {
                return reject(err);
            }
            return resolve({ fields, files });
        });
    });
}

function getFile (body, field) {
    const bodyFile = body.files[field];
    const value = bodyFile ? bodyFile[0] : null;
    return value || null;
}

module.exports = {
    parseRequest,
    getFile,
};

zip.js

const unzip = require('unzipper');
const fs = require('fs');

async function extractFiles (file) {
    const files = [];
    await fs.createReadStream(file.path).pipe(unzip.Parse()).on('entry', async entry => {
    // Cleanup system hidden files (or drop this code if not needed)
        if (
            entry.type !== 'File'
            || /^__MACOSX/.test(entry.path)
            || /.DS_Store/.test(entry.path)
        ) {
            entry.autodrain()
            return
        }
        const pathArr = entry.path.split('/');
        const path = entry.path;
        const buffer = await entry.buffer();
        files.push({ buffer, path, originalFilename: pathArr[pathArr.length - 1] });
    }).promise();
    return files;
}

module.exports = {
    extractFiles,
};

Usage:

  1. Start a server with node server
  2. Send your file in file field in request (key file in postman). Example in curl curl -XPOST -F 'file=@../ttrra-dsp-agency-api/banner.zip' 'localhost:3000/files')

Downsides:

  1. Unzipped files are stored in buffer so this method doesn't work great and is not recommended for big archives.
coockoo
  • 2,294
  • 17
  • 26
  • Thanks, I want to check it but I cannot use `unzipper` since its not supporting large file, only `yauzl` and the wrapper on top `extract-zip` , thanks –  Jul 17 '18 at 13:39
  • in addition it will be great if you can add the code which create the app folder if doesnt exist –  Jul 17 '18 at 13:59
-1

This is my code for uploading a file to express server.

//require express library
var express = require('express');
//require the express router
var router = express.Router();
//require multer for the file uploads
var multer = require('multer');

//File Upload

var storage = multer.diskStorage({
  // destino del fichero
  destination: function (req, file, cb) {
    cb(null, './uploads/logo')
  },
  // renombrar fichero
  filename: function (req, file, cb) {
    cb(null, file.originalname);
  }
});

var upload = multer({ storage: storage });

router.post("/", upload.array("uploads[]", 1), function (req, res) {
  res.json('Uploaded logo successfully');
});


module.exports = router; 
antzshrek
  • 9,276
  • 5
  • 26
  • 43
Ihor
  • 53
  • 8