如何在 Node.js 中使用 Zip 文件

作者选择了 开源精神疾病以作为 写给捐赠计划的一部分获得捐赠。

介绍

与文件一起工作是开发人员中最常见的任务之一. 随着文件的大小增加,它们开始在硬盘上占用大量空间。 迟早你可能需要将文件转移到其他服务器或从本地机器上上传送多个文件到不同的平台。 其中一些平台有文件大小限制,并且不会接受大型文件。 要绕过这一点,你可以将文件分组成一个ZIP文件。 ZIP文件是一种存档格式,可以使用 lossless compression 算法来包装和压缩文件。 该算法可以重建数据而不会损失任何数据。 在 Node.js,你可以使用 [adm-zip(https://github.com/cthackers/adm-zip) 模块来创建和阅读ZIP存档。

在本教程中,您将使用adm-zip模块来压缩,阅读和解压缩文件. 首先,您将使用adm-zip将多个文件合并到一个ZIP档案中。

前提条件

要遵循这个教程,你需要:

步骤1 - 设置项目

在此步骤中,您将创建您的项目的目录,并安装adm-zip作为依赖。 此目录是您将保留程序文件的地方。 您还将创建另一个包含文本文件和图像的目录。

创建一个名为zip_app的目录,使用以下命令:

1mkdir zip_app

使用cd命令导航到新创建的目录:

1cd zip_app

在目录中,创建一个package.json文件来管理项目依赖:

1npm init -y

选项-y创建了默认的package.json文件。

接下来,用npm install命令安装adm-zip:

1npm install adm-zip

執行命令後,「npm」會安裝「adm-zip」並更新「package.json」檔案。

接下来,创建一个名为测试的目录,并进入它:

1mkdir test && cd test

在这个目录中,你将创建三个文本文件,并下载一个图像. 三个文件将被填充了愚蠢的内容,使他们的文件大小更大. 这将有助于证明ZIP压缩,当你存档这个目录。

创建file1.txt并使用以下命令填写它用虚假内容:

1yes "dummy content" | head -n 100000 > file1.txt

命令会重复记录愚蠢的内容字符串。使用管道命令``,您将命令的输出发送用于命令的输入。命令会将给定的输入部分打印到标准输出中。n选项会指定应该写到标准输出中的行数。最后,您将输出重定向到使用>的新文件file1.txt

创建一个第二个文件,其字符串愚蠢的内容重复30万行:

1yes "dummy content" | head -n 300000 > file2.txt

创建另一个文件,使用愚蠢的内容字符串重复 600,000 行:

1yes "dummy content" | head -n 600000 > file3.txt

最后,下载一个图像到目录中使用‘curl’:

1curl -O https://assets.digitalocean.com/how-to-process-images-in-node-js-with-sharp/underwater.png

返回主项目目录,使用以下命令:

1cd ..

「......」將會將您移至母目錄,即「zip_app」。

您现在已经创建了项目目录,安装了adm-zip,并创建了一个文件目录以存档。

步骤 2 – 创建 ZIP 档案

在此步骤中,您将使用adm-zip来压缩和存档您在上一节创建的目录。

要存档目录,您将导入adm-zip模块,并使用该模块的addLocalFolder()方法将目录添加到adm-zip模块的ZIP对象中。

在您喜爱的文本编辑器中创建并打开一个新的文件 createArchive.js 本教程使用 nano,一个命令行文本编辑器:

1nano createArchive.js

接下来,请在你的createArchive.js文件中的adm-zip模块中要求:

1[label zip_app/createArchive.js]
2const AdmZip = require("adm-zip");

adm-zip模块提供了包含创建ZIP档案的方法的类。

由于在存档过程中经常会遇到大文件,所以您可能会最终阻止主线程,直到 ZIP 存档被保存。

在你的createArchive.js文件中,添加以下突出的代码:

 1[label zip_app/createArchive.js]
 2
 3const AdmZip = require("adm-zip");
 4
 5async function createZipArchive() {
 6  const zip = new AdmZip();
 7  const outputFile = "test.zip";
 8  zip.addLocalFolder("./test");
 9  zip.writeZip(outputFile);
10  console.log(`Created ${outputFile} successfully`);
11}
12
13createZipArchive();

「createZipArchive」是一個非同步函數,它從特定目錄中創建一個 ZIP 檔案。使其非同步的是您在函數標籤之前定義的「async」關鍵字。在該函數中,您創建了一個「adm-zip」模塊的實例,它提供了您可以使用的閱讀和創建檔案的方法。當您創建一個實例時,「adm-zip」創建了一個內存的 ZIP,您可以添加檔案或目錄。

接下来,您定义档案名称并将其存储在outputDir变量中. 若要将测试目录添加到内存档案中,您将从adm-zip中调用addLocalFolder()方法,并将目录路径作为参数。

添加目录后,您将从adm-zip中调用writeZip()方法,并使用包含 ZIP 档案名称的变量。

一旦完成,您会调用「console.log()」来记录已成功创建的 ZIP 文件。

最后,您将调用createZipArchive()函数。

在运行文件之前,将代码包装在一个 try...catch块中,以处理运行时错误:

 1[label zip_app/createArchive.js]
 2const AdmZip = require("adm-zip");
 3
 4async function createZipArchive() {
 5  try {
 6    const zip = new AdmZip();
 7    const outputFile = "test.zip";
 8    zip.addLocalFolder("./test");
 9    zip.writeZip(outputFile);
10    console.log(`Created ${outputFile} successfully`);
11  } catch (e) {
12    console.log(`Something went wrong. ${e}`);
13  }
14}
15
16createZipArchive();

试用块中,代码将尝试创建一个ZIP档案. 如果成功,则createZipArchive()函数将退出,跳过捕捉块。

保存和退出文件在nano中使用CTRL+X。输入y来保存更改,并通过在 Windows 上按ENTER或在 Mac 上按RETURN键来确认文件。

使用node命令运行createArchive.js文件:

1node createArchive.js

您将收到以下输出:

1[secondary_label Output]
2Created test.zip successfully

列出目录内容以查看是否已创建 ZIP 档案:

1ls

您将收到以下输出,在内容中显示档案:

1[secondary_label Output]
2createArchive.js node_modules package-lock.json
3package.json test test.zip

通过确认已创建 ZIP 档案,您将比较 ZIP 档案和测试目录文件大小,以查看压缩是否有效。

使用du命令检查test目录大小:

1du -h test

-h旗指示du以人可读的格式显示目录大小。

运行命令后,您将收到以下输出:

1[secondary_label Output]
215M	test

接下来,检查test.zip档案大小:

1du -h test.zip

du命令会记录下面的输出:

1[secondary_label Output]
2760K	test.zip

正如您所看到的,创建 ZIP 文件已将目录大小从 15 兆字节(MB)降低到 760 千兆字节(KB),这是一个巨大的区别。

现在你已经创建了一个ZIP档案,你已经准备好列出ZIP文件中的内容。

步骤 3 – 在 ZIP 档案中列出文件

在此步骤中,您将使用adm-zip读取并列出 ZIP 档案中的所有文件。 要做到这一点,您将使用 ZIP 档案路径实例化adm-zip模块,然后将调用该模块的getEntries()方法,该方法将返回一组对象。

在您最喜欢的文本编辑器中创建并打开readArchive.js:

1nano readArchive.js

在您的readArchive.js中,添加以下代码来读取和列出ZIP档案中的内容:

 1[label zip_app/readArchive.js]
 2const AdmZip = require("adm-zip");
 3
 4async function readZipArchive(filepath) {
 5  try {
 6    const zip = new AdmZip(filepath);
 7
 8    for (const zipEntry of zip.getEntries()) {
 9      console.log(zipEntry.toString());
10    }
11  } catch (e) {
12    console.log(`Something went wrong. ${e}`);
13  }
14}
15
16readZipArchive("./test.zip");

首先,您需要在「adm-zip」模块中。

接下来,您定义了readZipArchive()函数,这是一个不同步的函数。在该函数中,您创建了一个adm-zip的实例,用您想要阅读的ZIP文件的路径。文件路径由filepath参数提供。

閱讀檔案後,您定義了一個 for....of)聲明,該聲明重複在一個數列中的對象上,從「adm-zip」返回「getEntries()」方法,當它被召喚時。在每一次重複中,對象被分配到「zipEntry」變量上。在循環內,您將對象轉換為使用Node.js「toString()」方法表示對象的字符串,然後使用「console.log()」方法在控制台中登錄。

最后,您将用 ZIP 档案文件路径作为参数调用readZipArchive()函数。

保存和退出您的文件,然后使用以下命令运行文件:

1node readArchive.js

您将收到类似于以下的输出(编辑以简化):

 1[secondary_label Output]
 2{
 3    "entryName": "file1.txt",
 4    "name": "file1.txt",
 5    "comment": "",
 6    "isDirectory": false,
 7    "header": {
 8    	...
 9    },
10    "compressedData": "<27547 bytes buffer>",
11    "data": "<null>"
12}
13...

控制台将记录四个对象,其他对象已被编辑,以保持教程简短。

档案中的每个文件都以一个类似于前一个输出中的对象表示. 要获取每个文件的文件名,您需要访问名称属性。

在您的readArchive.js文件中,添加以下突出的代码来访问每个文件名:

 1[label zip_app/readArchive.js]
 2const AdmZip = require("adm-zip");
 3
 4async function readZipArchive(filepath) {
 5  try {
 6    const zip = new AdmZip(filepath);
 7
 8    for (const zipEntry of zip.getEntries()) {
 9      console.log(zipEntry.name);
10    }
11  } catch (e) {
12    console.log(`Something went wrong. ${e}`);
13  }
14}
15
16readZipArchive("./test.zip");

保存并退出文本编辑器. 现在,用节点命令再次运行文件:

1node readArchive.js

运行文件结果在以下输出:

1[secondary_label Output]
2file1.txt
3file2.txt
4file3.txt
5underwater.png

输出现在在 ZIP 档案中记录每个文件的文件名。

您现在可以阅读并列出每个文件在ZIP档案中,在下一节中,您将添加一个文件到现有的ZIP档案中。

步骤 4 – 将文件添加到现有档案中

在此步骤中,您将创建一个文件,并将其添加到您之前创建的 ZIP 档案中,而无需提取它. 首先,您将通过创建一个adm-zip实例来读取 ZIP 档案。

创建另一个file4.txt文件,包含重复60万行的愚蠢内容:

1yes "dummy content" | head -n 600000 > file4.txt

在文本编辑器中创建并打开updateArchive.js:

1nano updateArchive.js

请在adm-zip模块和fs模块中,允许您在updateArchive.js文件中的文件中工作:

1const AdmZip = require("adm-zip");
2const fs = require("fs").promises;

您需要在 promise-based版本中使用fs模块版本,这允许您编写非同步代码。

接下来,在您的updateArchive.js文件中,添加以下突出的代码,将新文件添加到ZIP档案中:

 1[label zip_app/updateArchive.js]
 2const AdmZip = require("adm-zip");
 3const fs = require("fs").promises;
 4
 5async function updateZipArchive(filepath) {
 6  try {
 7    const zip = new AdmZip(filepath);
 8
 9    content = await fs.readFile("./file4.txt");
10    zip.addFile("file4.txt", content);
11    zip.writeZip(filepath);
12    console.log(`Updated ${filepath} successfully`);
13  } catch (e) {
14    console.log(`Something went wrong. ${e}`);
15  }
16}
17
18updateZipArchive("./test.zip");

「updateZipArchive」是一个非同步函数,它在文件系统中读取文件并将其添加到现有的 ZIP。在该函数中,您将创建一个adm-zip的实例,在filepath中的ZIP档案路径作为参数。接下来,您将召唤fs模块的readFile()方法来读取文件在文件系统中。readFile()方法将返回一个承诺,您将用Wait关键字解决(Wait仅适用于非同步函数)。

接下来,您从adm-zip 中调用addFile()方法. 该方法需要两个参数. 第一个参数是您要添加到档案中的文件名,第二个参数是包含该文件内容的缓冲对象,而readFile()方法读取的文件内容。

随后,您将召唤adm-zip模块的writeZip()方法,以便在 ZIP 档案中保存和编写新的更改。

最后,您将用 Zip 档案文件路径作为参数调用 updateZipArchive() 函数。

保存并退出您的文件. 使用以下命令运行 updateArchive.js 文件:

1node updateArchive.js

你会看到这样的输出:

1[secondary_label Output]
2Updated ./test.zip successfully

现在,确认ZIP档案中包含新文件。运行readArchive.js文件以使用以下命令列出ZIP档案中的内容:

1node readArchive.js

您将收到以下输出:

1file1.txt
2file2.txt
3file3.txt
4file4.txt
5underwater.png

这确认该文件已被添加到ZIP。

现在您可以将文件添加到现有档案中,您将在下一节中提取档案。

步骤 5 – 提取 Zip 档案

在此步骤中,您将读取并将 ZIP 档案中的所有内容提取到一个目录中. 为了提取 ZIP 档案,您将用档案文件路径实例adm-zip

在文本编辑器中创建并打开extractArchive.js:

1nano extractArchive.js

请在您的extractArchive.js文件中的adm-zip模块和path模块中要求:

1[label zip_app/extractArchive.js]
2const AdmZip = require("adm-zip");
3const path = require("path");

路径模块为处理文件路径提供了有用的方法。

仍然在您的extractArchive.js文件中,添加以下突出的代码来提取档案:

 1[label zip_app/extractArchive.js]
 2const AdmZip = require("adm-zip");
 3const path = require("path");
 4
 5async function extractArchive(filepath) {
 6  try {
 7    const zip = new AdmZip(filepath);
 8    const outputDir = `${path.parse(filepath).name}_extracted`;
 9    zip.extractAllTo(outputDir);
10
11    console.log(`Extracted to "${outputDir}" successfully`);
12  } catch (e) {
13    console.log(`Something went wrong. ${e}`);
14  }
15}
16
17extractArchive("./test.zip");

extractArchive() 是一个不同步的函数,它采用包含 ZIP 档案的文件路径的参数。 在该函数中,您将 adm-zip' 与由 filepath' 参数提供的 ZIP 档案文件路径实例化。

接下来,您将定义一个 模板字面。在模板字面位置持有人(${})中,您将从文件路径的 path() 模块中调用 parse()方法。parse()方法返回一个对象。 要获得没有文件扩展的 ZIP 文件的名称,您将name属性附加到对象中,由parse() 方法返回。 一旦归还了档案名,模板字面上将该值与 _extracted' 字符串交互。 然后,该值存储在 putDir' 变量中。 这将是提取的目录的名称。

接下来,您将调用adm-zip模块的extractAllTo方法,以存储在outputDir中的目录名称,以提取目录中的内容。

最后,您将与 ZIP 档案路径呼叫extractArchive()函数。

保存您的文件,离开编辑器,然后使用以下命令运行extractArchive.js文件:

1node extractArchive.js

您将获得以下输出:

1[secondary_label Output]
2Extracted to "test_extracted" successfully

确认已创建包含 ZIP 内容的目录:

1ls

您将获得以下输出:

1[secondary_label Output]
2createArchive.js file4.txt package-lock.json
3readArchive.js test.zip updateArchive.js
4extractArchive.js node_modules package.json
5test test_extracted

现在,导航到包含提取的内容的目录:

1cd test_extracted

在目录中列出内容:

1ls

您将获得以下输出:

1[secondary_label Output]
2file1.txt file2.txt file3.txt file4.txt underwater.png

现在您可以看到目录中包含原始目录中的所有文件。

您现在已经将 ZIP 档案内容提取到目录中。

结论

在本教程中,您创建了一个ZIP档案,列出其内容,将新文件添加到档案中,并使用adm-zip模块将其所有内容提取到目录中。

若要了解更多有关「adm-zip」模块的信息,请参阅 adm-zip 文档。 若要继续构建您的 Node.js 知识,请参阅 How To Code in Node.js series

Published At
Categories with 技术
comments powered by Disqus