使用Node.js压缩整个目录

169

我需要使用Node.js压缩整个目录。我目前正在使用node-zip,但每次运行该过程时,它都会生成一个无效的ZIP文件(如此Github问题所示)。

是否有另一种更好的Node.js选项可以让我压缩目录?

编辑:最终我使用了archiver

writeZip = function(dir,name) {
var zip = new JSZip(),
    code = zip.folder(dir),
    output = zip.generate(),
    filename = ['jsd-',name,'.zip'].join('');

fs.writeFileSync(baseDir + filename, output);
console.log('creating ' + filename);
};

参数的示例值:

dir = /tmp/jsd-<randomstring>/
name = <randomstring>

更新:针对那些询问我使用的实现方式的人,这里是我的下载器链接


6
有人在Twitter上建议使用child_process API,然后简单地调用系统的ZIP:http://nodejs.org/api/child_process.html - commadelimited
1
我尝试了child_process方法。它有两个注意点。1)unix的zip命令在压缩文件时会包含当前工作目录的所有父文件夹层级,这对你可能没问题,但对我来说不行。而且,在child_process中更改当前工作目录似乎并不影响结果。2)为了解决这个问题,你需要使用pushd跳转到要压缩的文件夹并使用zip -r,但由于pushd是bash内置命令而不是/bin/sh,所以你需要使用/bin/bash。在我的特定情况下,这是不可能的。提前告诉你一声。 - johnozbay
2
@johnozbay node的child_process.execAPI允许您指定要运行命令的cwd。更改CWD确实解决了父文件夹层次结构的问题。它还解决了不需要pushd的问题。我完全推荐使用child_process。 - Govind Rai
1
使用child_process API的本地Node.js解决方案。只需两行代码,无需第三方库。 - Govind Rai
@GovindRai 非常感谢! - johnozbay
14个回答

192

最终我使用了archiver库。效果非常好。

示例

var file_system = require('fs');
var archiver = require('archiver');

var output = file_system.createWriteStream('target.zip');
var archive = archiver('zip');

output.on('close', function () {
    console.log(archive.pointer() + ' total bytes');
    console.log('archiver has been finalized and the output file descriptor has closed.');
});

archive.on('error', function(err){
    throw err;
});

archive.pipe(output);

// append files from a sub-directory, putting its contents at the root of archive
archive.directory(source_dir, false);

// append files from a sub-directory and naming it `new-subdir` within the archive
archive.directory('subdir/', 'new-subdir');

archive.finalize();

1
好像没有关于如何做这个的例子,您介意分享一下您的做法吗? - Sinetheta
1
非常遗憾,目前归档程序还不支持Unicode字符作为文件名。已向https://github.com/ctalkington/node-archiver/issues/90报告该问题。 - esengineer
2
如何递归地包含所有文件和目录(包括隐藏的文件/目录)? - Ionică Bizău
12
Archiver现在更加简单易用。不再需要使用bulk()方法,现在可以使用directory()方法:https://www.npmjs.com/package/archiver#directory-dirpath-destpath-data - Josh Feldman
15
.bulk已经过时。 - chovy
显示剩余6条评论

124

我不打算展示新内容,只是想为那些像我一样喜欢Promise的人总结上面的解决方案。

const archiver = require('archiver');

/**
 * @param {String} sourceDir: /some/folder/to/compress
 * @param {String} outPath: /path/to/created.zip
 * @returns {Promise}
 */
function zipDirectory(sourceDir, outPath) {
  const archive = archiver('zip', { zlib: { level: 9 }});
  const stream = fs.createWriteStream(outPath);

  return new Promise((resolve, reject) => {
    archive
      .directory(sourceDir, false)
      .on('error', err => reject(err))
      .pipe(stream)
    ;

    stream.on('close', () => resolve());
    archive.finalize();
  });
}

希望这能帮助到某些人。


2
无法解压缩该zip文件。操作不允许。 - Jake
@ekaj_03 请确保您拥有指定目录的足够权限。 - D.Dimitrioglo
1
@D.Dimitrioglo 一切都好。这是源目录的问题。谢谢 :) - Jake
我发现在这个链接中,添加文件并在Nodejs中进行压缩的最简单方法是:https://jsonworld.wordpress.com/2019/09/07/how-to-zip-and-download-files-in-nodejs-express/ - Soni Kumari
1
谢谢!这对我很有帮助!节省了时间!这是一个使用 import from 的改编版本:https://dev59.com/kmUo5IYBdhLWcg3w9TTb#70958944 - Jesus is Lord
显示剩余6条评论

47

使用Node的原生child_process api来实现此操作。

不需要第三方库。只需要两行代码。

const child_process = require("child_process");
child_process.execSync(`zip -r <DESIRED_NAME_OF_ZIP_FILE_HERE> *`, {
  cwd: <PATH_TO_FOLDER_YOU_WANT_ZIPPED_HERE>
});

上面的示例展示了同步API。如果需要异步行为,您也可以使用child_process.exec(path, options, callback)。除了cwd之外,还有很多选项可供指定以进一步微调您的请求。


如果您没有ZIP工具:

这个问题是特别针对用于归档/压缩目的的zip实用程序提问的。因此,此示例假设您已在系统上安装了zip实用程序。为了完整起见,某些操作系统可能不会默认安装此实用程序。在这种情况下,您至少有三个选择:

  1. 使用本地于您的平台的归档/压缩实用程序

    将上面的Node.js代码中的 shell 命令替换为来自您的系统的代码。例如,Linux发行版通常会带有tar/gzip实用程序:

    tar -cfz <DESIRED_NAME_OF_ZIP_FILE_HERE> <PATH_TO_FOLDER_YOU_WANT_ZIPPED_HERE>

    这是一个不错的选择,因为您不需要在操作系统上安装任何新内容或管理其他依赖项(这本质上是本回答的整个重点)。

  2. 获取您的操作系统/分发版的zip二进制文件。

    例如,在Ubuntu上:apt install zip

    ZIP实用程序已经经过了几十年的尝试和测试,它是相当普遍且安全的选择。快速搜索一下Google或访问创建者Info-ZIP的网站以获取可下载的二进制文件。

  3. 使用第三方库/模块(在NPM上有很多)。

    我不喜欢此选项。但是,如果您确实不关心理解本地方法并且引入新的依赖关系不是问题,则这也是一个有效的选项。


28
仅适用于拥有“zip”系统的计算机。 - janpio
5
为了避免在我的项目中使用许多外部库,我选择了这个解决方案。 - EAzevedo
假设给定的库已安装在底层平台上,这将违背 package.json 依赖清单的目的。更重要的是,如果 Zip 只在 MacOS 上可用,而您部署到 Linux 环境中,那么您注定会失败 :D - perepm
提醒一下那些认为这个解决方案不够好的人,因为它依赖于zip二进制文件:这个问题特别询问如何压缩文件,而不是要求跨平台的归档解决方案。最后一段专门用于获取跨平台的zip实用程序。 - Govind Rai
有没有一种方法可以在API路由中流式传输输入并将生成的zip归档文件流式传输出来?我正在使用Node库进行流式处理以流式传输结果,但是我想知道是否可以使用exec而不是使用该工具。 - Ahmad Ghadiri
显示剩余7条评论

20

这是另一个可以在一行中压缩文件夹的库: zip-local

var zipper = require('zip-local');

zipper.sync.zip("./hello/world/").compress().save("pack.zip");

5
非常有效,与网上或前面提到的其他数十种工具不同,它们总是为我生成一个大小为0字节的文件。 - Sergey Pleshakov

13

Archive.bulk现在已被弃用,新方法是glob

var fileName =   'zipOutput.zip'
var fileOutput = fs.createWriteStream(fileName);

fileOutput.on('close', function () {
    console.log(archive.pointer() + ' total bytes');
    console.log('archiver has been finalized and the output file descriptor has closed.');
});

archive.pipe(fileOutput);
archive.glob("../dist/**/*"); //some glob pattern here
archive.glob("../dist/.htaccess"); //another glob pattern
// add as many as you like
archive.on('error', function(err){
    throw err;
});
archive.finalize();

2
我在思考这个问题,他们说bulk已经过时了,但没有建议使用哪个函数代替它。 - jarodsmk
1
你如何指定“源”目录? - Dreams
尝试一下以下方法:https://jsonworld.wordpress.com/2019/09/07/how-to-zip-and-download-files-in-nodejs-express/ - Soni Kumari
2020年:archive.directory()更加简单易用! - OhadR

9
要包含所有文件和目录:
archive.bulk([
  {
    expand: true,
    cwd: "temp/freewheel-bvi-120",
    src: ["**/*"],
    dot: true
  }
]);

它使用 node-glob(https://github.com/isaacs/node-glob) 库进行匹配,因此任何与之兼容的匹配表达式都可以使用。

.bulk已经被弃用。 - Mohamad Hamouday

4
将结果通过响应对象传输(在需要下载zip而不是本地存储的场景下)
 archive.pipe(res);

Sam给出的访问目录内容的提示对我很有用。

src: ["**/*"]

4

由于 archiver 长时间内不兼容新版 webpack,我建议使用 zip-lib

var zl = require("zip-lib");

zl.archiveFolder("path/to/folder", "path/to/target.zip").then(function () {
    console.log("done");
}, function (err) {
    console.log(err);
});

你有一个将 zip-lib 集成到我的 webpack 项目的示例吗? - C Alonso C Ortega

3

就目前而言,我正在使用AdmZip,它工作得很好:

const AdmZip = require('adm-zip');
export async function archiveFile() {
  try {
    const zip = new AdmZip();
    const outputDir = "/output_file_dir.zip";
    zip.addLocalFolder("./yourFolder")
    zip.writeZip(outputDir);
  } catch (e) {
    console.log(`Something went wrong ${e}`);
  }
}

3
我找到了一个小型图书馆,它包含了你所需要的内容。
npm install zip-a-folder

const zipAFolder = require('zip-a-folder');
await zipAFolder.zip('/path/to/the/folder', '/path/to/archive.zip');

https://www.npmjs.com/package/zip-a-folder


能否在创建zip文件夹时添加参数,例如压缩级别和大小?如果可以,应该怎么做? - Trang D
因为zip-a-folder不是一个有效的标识符,所以给它点个踩。 - Cipi

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