0%

Node 压缩文件夹并发送邮件

使用jszip压缩文件夹,使用nodemail发送邮件。

jszip压缩文件

压缩文件夹我们使用jszip。官网:stuark jszip

首先在项目中安装jszip

1
2
3
npm install jszip;
# 或者使用yarn
yarn add jszip

然后我们来创建一个用来测试压缩的文件夹,里面包含子文件夹:

1
2
3
4
5
testZip
├── 1.json
├── 3.json
└── innerDir
└── canvas.html

我们希望压缩文件夹testZip里面的所有文件夹以及文件。

首先定义一下路径:

1
2
3
4
5
6
7
8
9
const fs = require('fs')
const path = require('path')
const JSZIP = require('jszip')
// 期望压缩的文件夹路径
const dirPath = path.resolve(__dirname, 'testZip');
// 生成的压缩包名称
const name = 'testZip';
// 生成压缩包的路径
const zipPath = path.resolve(__dirname, './');

然后我们规划下方法,我们需要一个压缩方法,期望传递的是目标文件夹的路径、压缩包的名称、生成压缩包后的存放路径:

1
2
3
function compressedDir(dirPath, name, zipPath) {

}

我们是要压缩一个文件夹,这个文件夹里面可能包含着文件和文件夹,所以我们需要递归读取文件夹。

  • 遇到文件夹里面的文件夹的话,需要使用zip.folder()给压缩包创建文件夹,然后进入文件夹继续读取。
  • 遇到文件的话,直接使用zip.file()来放入压缩包中

然后我们有如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* 深层递归读取文件夹中的文件
* @param zip
* @param dirPath
*/
function readDirDeep(zip, dirPath) {
const files = fs.readdirSync(dirPath);
for (const fileName of files) {
const filePath = path.join(dirPath, fileName);
const stat = fs.statSync(filePath);
if (!stat.isDirectory()) {
zip.file(fileName, fs.readFileSync(filePath));
continue;
}
const folderZip = zip.folder(fileName);
readDirDeep(folderZip, filePath);
}
}

然后开始继续完善compressedDir方法。

我们需要考虑一下几点:

  • 判断传递的路径,
    • 如果是非文件夹,那么就直接放入压缩包即可。
    • 如果是文件夹,调用readDirDeep来递归读取。
  • 将读取好的zip对象调用zip.generateAsync()函数进行压缩包文件流的创建。
  • 将压缩包的流内容写入存储压缩包的路径,获得压缩包文件,结束。

我们有如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/**
* 压缩文件夹
* @param dirPath 压缩的文件夹路径
* @param name 产生的压缩包名称
* @param zipDir 压缩包存放的路径
* @returns {Promise<string>} zipPath 压缩包的文件路径
*/
async function compressedDir(dirPath, name, zipDir = null ) {
if (!zipDir) {
// 默认存放zip的路径为当前路径
zipDir = path.resolve(__dirname, './');
}
const zip = new JSZIP();
const stat = fs.statSync(dirPath);
if (!stat.isDirectory()) {
console.log(path.parse(dirPath))
const {base: fileName = 'unKnow'} = path.parse(dirPath);
zip.file(fileName, fs.readFileSync(dirPath));
} else {
readDirDeep(zip, dirPath)
}
const zipContent = await zip.generateAsync({
type: 'nodebuffer',
compression: 'DEFLATE',
compressionOptions: {
level: 9,
}
});
const zipPath = path.join(zipDir, `${name}.zip`);
fs.writeFileSync(zipPath, zipContent, 'utf-8')
return zipPath;
}

然后来运行一个例子进行测试:

1
2
3
4
5
6
7
8
9
10
try {
const dirPath = path.resolve(__dirname, 'testZip');
const name = 'testZip';
const zipDir= path.resolve(__dirname, './');
compressedDir(dirPath, name,zipDir).then(zipPath => {
console.log('压缩完成,路径为:', zipPath)
});
} catch (e) {
console.log(e);
}

然后检查是否正确生成了对应的zip文件,并且需要解压缩zip看看里面的内容是否正常。

发送邮件

发送邮件我们使用nodemailer,官网:nodemailer

安装:

1
2
3
npm install nodemailer
# 或使用yarn
yarn add nodemailer

首先我们需要一个传输器对象:

1
2
3
4
const nodemailer = require('nodemailer')
const fs = require('fs')
// 传输器对象
const transporter = nodemailer.createTransport(options)

在使用传输器前我们需要了解下SMTP(Simple Mail Transfer Protocol)协议。参考:维基百科 SMTP

几乎每个电子邮件提供商都支持基于SMTP协议的邮件发送。nodemailer中主要的传输方式就是SMTP协议。

SMTP是一个”推送“协议,它不允许根据需要从远程服务器上”拉取“消息。要拉取消息到客户端,邮件客户端必须使用POP3或IMAP。

所以我们主要使用SMTP来发送邮件。

我们来看下创建传输器的时候需要啥:

  • port 端口
  • host 主机
  • auth 身份验证
  • secure 是否使用TLS,如果端口是465,需要设置为true;如果是587或25,就设置为false

我这里使用网易的126邮箱。它在邮箱的设置那里有SMTP的设置,并可以提供客户端授权密码,这样可以随时取消授权,也不会暴露我邮箱本来的密码,非常nice。

我们在126邮箱的设置 -> POP3/SMTP/IMAP开启SMTP服务。并在授权管理这里设置授权密码。

服务器地址:smtp.126.com。安全上是支持SSL链接的,所以端口的话是465

然后回到代码,我们创建传输器:

1
2
3
4
5
6
7
8
9
10
11
12
13
const nodemailer = require('nodemailer')
const fs = require('fs')

const transporter = nodemailer.createTransport({
host: 'smtp.126.com',
secure: true,
port: 465,
auth: {
user: 'tonycs_y@126.com',
// 客户端授权密码
pass: 'xxx'
}
})

然后封装方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
async function sendMail({
// 邮件标题
subject,
// 邮件内容
text,
// 文件
attachments
}) {
const message = {
from: '"tonycs" <tonycs_y@126.com>',
to: 'target@mail.com',
subject,
text,
html: text,
attachments,
}
const info = await transporter.sendMail(message)
console.log(`--- 邮件发送成功 ✅ ${info.messageId}`);
}

这里主要说下附件的参数,我们可以将附件的路径转换为文件流,传递给attachments。需要注意的是,这里附件的名称是小写的filename

调用这个方法:

1
2
3
4
5
6
7
8
sendMail({
subject: `测试邮件`,
text: `邮件正文`,
attachments: {
filename: `附件.zip`,
content: fs.createReadStream(zipPath)
}
}).then();

然后查看目标邮箱中是否有对应的邮件。


完成了这个流程,我们就可以很容易的构造一个备份脚本。将对应数据存入本地的文件夹,然后压缩文件夹,再以附件的形式发送邮件,一步到位。

码字辛苦,打赏个咖啡☕️可好?💘