5

使用archiver.js(对于Node.js),我需要从递归(多子目录)存档中排除图像。这是我的代码:

const zip = archiver('zip', { zlib: { level: 9 } });
const output = await fs.createWriteStream(`backup/${fileName}.zip`);
res.setHeader('Content-disposition', `attachment; filename=${fileName}.zip`);
res.setHeader('Content-type', 'application/download');
output.on('close', function () {
  res.download(`backup/${fileName}.zip`, `${fileName}.zip`);
});
output.on('end', function () {
  res.download(`backup/${fileName}.zip`, `${fileName}.zip`);
});
zip.pipe(output);
zip.glob('**/*',
  {
    cwd: 'user_uploads',
    ignore: ['*.jpg', '*.png', '*.webp', '*.bmp'],
  },
  {});
zip.finalize();

问题是它没有排除忽略文件。如何更正语法?

4

2 回答 2

2

Archiver使用Readdir-Glob进行 globbing,它使用minimatch进行匹配。

Readdir-Glob ( ) 中的匹配node-readdir-glob/index.js#L147是针对包括路径名在内的完整文件名完成的,它不允许我们应用matchBase几乎只是完整路径的基本名称的选项。

为了使其正常工作,您有 2 个选项:


1.让你的glob排除文件扩展名

您可以使用 glob 否定转换您的 glob 表达式以排除您不想包含在存档文件中的所有文件扩展名!(...),它将包括除与否定表达式匹配的所有内容:

zip.glob(
  '**/!(*.jpg|*.png|*.webp|*.bmp)',
  {
    cwd: 'user_uploads',
  },
  {}
);

2. 使minimatch使用完整的文件路径名

要使minimatch在我们无法设置matchBase选项的情况下工作,我们必须包含匹配的目录 glob 才能工作:

zip.glob(
  '**/*',
  {
    cwd: 'user_uploads',
    ignore: ['**/*.jpg', '**/*.png', '**/*.webp', '**/*.bmp'],
  },
  {}
);

行为

Readdir-Glob的这种行为对于选项有点令人困惑ignore

选项

ignore: Glob 模式或 Glob 模式数组以排除匹配项。如果文件或文件夹至少与提供的模式之一匹配,则不会返回。它不会阻止返回文件夹内容中的文件。

这意味着igrore项目必须是必须包含整个路径/文件表达式的实际全局表达式。当我们指定*.jpg时,它将仅匹配根目录中的文件,而不匹配子目录中的文件。如果我们想将 JPG 文件排除在目录树的深处,我们必须使用包含所有目录模式以及文件扩展名模式 **/*.jpg.

仅在子目录中排除

如果您只想在特定子目录中排除某些文件扩展名,可以使用如下否定模式将子目录添加到路径中:

// The glob pattern '**/!(Subdir)/*.jpg' will exclude all JPG files,
// that are inside any 'Subdir/' subdirectory.

zip.glob(
  '**/*',
  {
    cwd: 'user_uploads',
    ignore: ['**/!(Subdir)/*.jpg'],
  },
  {}
);
于 2021-02-07T11:25:19.947 回答
-1

以下代码正在使用此目录结构:

node-app
    |
    |_ upload
         |_subdir1
         |_subdir2
         |_...

代码__dirname中是node-app目录(node-app是您的应用程序所在的目录)。该代码是对https://www.archiverjs.com/段落Quick Start中代码的改编

// require modules
const fs = require('fs');
const archiver = require('archiver');

// create a file to stream archive data to.
const output = fs.createWriteStream(__dirname + '/example.zip');
const archive = archiver('zip', {
  zlib: { level: 9 } // Sets the compression level.
});

// listen for all archive data to be written
// 'close' event is fired only when a file descriptor is involved
output.on('close', function() {
  console.log(archive.pointer() + ' total bytes');
  console.log('archiver has been finalized and the output file descriptor has closed.');
});

// This event is fired when the data source is drained no matter what was the data source.
// It is not part of this library but rather from the NodeJS Stream API.
// @see: https://nodejs.org/api/stream.html#stream_event_end
output.on('end', function() {
  console.log('Data has been drained');
});

// good practice to catch warnings (ie stat failures and other non-blocking errors)
archive.on('warning', function(err) {
  if (err.code === 'ENOENT') {
    // log warning
  } else {
    // throw error
    throw err;
  }
});

// good practice to catch this error explicitly
archive.on('error', function(err) {
  throw err;
});

// pipe archive data to the file
archive.pipe(output);

    
archive.glob('**', 
             {
                cwd: __dirname + '/upload',
                ignore: ['*.png','*.jpg']}
);

// finalize the archive (ie we are done appending files but streams have to finish yet)
// 'close', 'end' or 'finish' may be fired right after calling this method so register to them beforehand
archive.finalize();

glob是“global”的缩写,因此您可以使用*文件名中的通配符(https://en.wikipedia.org/wiki/Glob_(programming))。因此,一种可能的准确通配符表达式是*.jpg, *.png,... ,具体取决于您要排除的文件类型。通常,星号通配符 * 替换文件系统上下文中的任意数量的文字字符或空字符串(文件和目录名称,https://en.wikipedia.org/wiki/Wildcard_character

另请参阅node.js - 使用归档器生成一个空 zip 的归档文件夹

于 2021-02-02T17:48:22.203 回答