使用node.js生成一个有密码保护的ZIP文件

15

我需要在node.js中创建一个密码保护的ZIP文件。

我正在使用“node-zip”模块,但不幸的是它不支持密码保护:

var zip = new require('node-zip')();
zip.file('test.file', 'hello there');
var data = zip.generate({base64:false,compression:'DEFLATE'});

在寻找其他节点模块来创建ZIP文件时,我没有找到支持密码保护的任何模块。


1
请搜索"在Node.js中创建ZIP文件",尝试访问https://dev59.com/1m025IYBdhLWcg3w3J1H。阅读第一个链接后,似乎生成一个命令行工具是解决问题的方法。 - Mörre
1
我找到了一个用于node-archiver创建加密zip的插件(https://github.com/ksoichiro/node-archiver-zip-encryptable)。它也可以在流上工作。但是我没有进行测试。 - Amit Kumar Gupta
5个回答

17

如果您在Linux上工作,那么可以通过zip(大多数Linux发行版中的命令行实用程序)来完成此操作。只需在应用程序中包含以下内容即可。

spawn = require('child_process').spawn;
zip = spawn('zip',['-P', 'password' , 'archive.zip', 'complete path to archive file']);
zip .on('exit', function(code) {
...// Do something with zipfile archive.zip
...// which will be in same location as file/folder given
});
如果你想要压缩文件夹,只需要在文件夹路径前加上另一个参数 '-r',而不是文件路径。
请记住这会开启一个独立的线程,不会阻塞主进程。更多有关于child_process的信息可以在这里http://nodejs.org/api/child_process.html找到。

1
你的解决方案看起来没问题,但我需要动态创建文件,也就是说这些文件在文件系统中不存在(它们包含敏感数据)。 据我所知,你不能将带有内容的文件结构传递给zip命令,对吗? - greuze
2
你的意思是压缩变量中的任何数据,而不是文件。Zip还接受单个破折号(“-”)作为要压缩的文件名,在这种情况下,它将从标准输入读取文件。您可以将文件内容直接写入生成的子进程zip.stdin.write(data)的stdin中。在同一页上查找stdin示例。http://nodejs.org/api/child_process.html#child_process_child_stdin - user568109
我需要在zip文件中写入两个具有确定名称的文件(我尝试使用“ - ”作为文件名,但名称仍然是“ - ”)。恐怕我必须使用临时目录来解压使用node-zip创建的文件,然后再使用zip命令进行压缩并设置密码 :( - greuze

14

对于像我一样到这里来的任何人,我尝试了node中的几个软件包,但最终选择了这一个:https://www.npmjs.com/package/minizip-asm.js

它支持密码(使用AES),非常容易使用。 我很惊讶它没有那么多下载量,因为这是我发现唯一支持密码的。


没有为TypeScript声明类型 - undefined

13

虽然archiver有typescript类型声明,但archiver-zip-encrypted没有,并且必须手动添加,可以查看开放的问题:https://github.com/artem-karpenko/archiver-zip-encrypted/issues/31 - undefined

0
我正在使用的解决方案(我不知道更好的方法)是:
var contenido1 = 'contenido super secreto';
var contenido2 = 'otro contenido';
var password = 'pass';
var nombreFichero = 'fichero'

var nodezip = new require('node-zip')();
var fs = require("fs");
nodezip.file('test1.txt', contenido1);
nodezip.file('test2.txt', contenido2);
var data = nodezip.generate({base64:false,compression:'DEFLATE'});
fs.writeFile(nombreFichero + '.zip', data, 'binary');

var exec = require('child_process').exec,
    child;

child = exec('unzip ' + nombreFichero + '.zip -d ' + nombreFichero +
             ' && zip -junk-paths --password ' + password + ' ' + nombreFichero + '-p.zip ' + nombreFichero + '/*' +
             ' && rm -rf ' + nombreFichero + ' && rm -f ' + nombreFichero + '.zip',
  function (error, stdout, stderr) {
    console.log('stdout: ' + stdout);
    console.log('stderr: ' + stderr);
    if (error !== null) {
      console.log('exec error: ' + error);
    }
});

它生成一个临时的无密码压缩文件,然后通过几个命令进行解压、加密压缩并移除临时文件。

0
对于任何想要在NextJS(也兼容NodeJS)服务器端创建密码保护的zip文件的人,请参考以下内容:
您可以将此代码放置在NextJS的api文件夹下。
import { NextApiRequest, NextApiResponse } from 'next';
import formidable from 'formidable';
import { join } from 'path';
import { spawn } from 'child_process';

export const config = {
  api: {
    bodyParser: false,
  },
};

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
  return new Promise<void>((resolve, reject) => {
    try {
      const form = new formidable.IncomingForm();

      form.parse(req, async (err, fields, files) => {
        if (err) {
          console.error('Error parsing form data:', err);
          res.status(500).json({ error: 'Failed to parse form data' });
          reject(err);
          return;
        }

        const password: string = fields.password.toString();
        const myFile = fields.myFile;

        if (!myFile) {
          res.status(400).json({ error: 'File not found in request' });
          return;
        }

        // Decode the Base64 string into a buffer
        const fileBuffer = Buffer.from(Array.isArray(myFile) ? myFile[0] : myFile, 'base64');

        // Save the file buffer to a temporary file
        const tempFilePath = join(process.cwd(), 'zippedFiles', 'temp_file');
        require('fs').writeFileSync(tempFilePath, fileBuffer);

        // Create a password-protected zip using the `zip` command with `-j` flag
        // `-j` flag will store just the file, not the directories
        const zipFilePath = join(process.cwd(), 'zippedFiles', 'protected.zip');
        const zipProcess = spawn('zip', ['-j', '-P', password, zipFilePath, tempFilePath]);

        zipProcess.on('exit', (code) => {
          if (code === 0) {
            // Zip file creation successful
            res.status(200).json({ zipPath: 'protected.zip' });
            resolve();
          } else {
            // Zip file creation failed
            console.error('Error creating password-protected zip file');
            res.status(500).json({ error: 'Failed to create zip file' });
            reject(new Error('Failed to create zip file'));
          }

          // Delete the temporary file
          require('fs').unlinkSync(tempFilePath);
        });
      });
    } catch (error) {
      console.error('Error generating password-protected zip:', error);
      res.status(500).json({ error: 'Failed to generate zip file' });
      reject(error);
    }
  });
}

我还提供了用于端到端实现的示例客户端代码。
import axios from 'axios';

const createPasswordProtectedZip = async (selectedFile: File, password: string) => {

  let data = undefined
  const base64Data = await readFileAsBase64(selectedFile);

  const formData = new FormData();
  formData.append('myFile', base64Data);
  formData.append('password', password);


  data = await axios.post('/api/download-zip', formData).then((res: any) => {
    console.log('File response:', res.data);
    return res.data
  }).catch((e: any) => {
    console.log('File error:', e);
    return undefined
  });

  return data

};

const readFileAsBase64 = (file: File) => {
  return new Promise<string>((resolve, reject) => {
    const reader = new FileReader();

    reader.onload = () => {
      const result = reader.result;
      if (typeof result === 'string') {
        const base64Data = result.split(',')[1];
        if (base64Data) {
          resolve(base64Data);
        } else {
          reject(new Error('Failed to read the file.'));
        }
      } else {
        reject(new Error('Failed to read the file.'));
      }
    };

    reader.onerror = (error) => {
      reject(error);
    };

    reader.readAsDataURL(file);
  });
};
export default createPasswordProtectedZip;

请注意,我正在使用TypeScript。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接