定时导出数据库数据并备份到gitee和邮箱

256 阅读9分钟

说在前面

现在大多数人都有自己的服务器,也会利用服务器搭建一些个人网站,那么有多少人会定时备份自己服务器的数据库数据呢?为了防止突发情况的发生,我们还是需要定时对自己数据库数据进行备份,今天我们一起来看看怎么使用node编写一个备份脚本,一键将数据备份到gitee和邮箱中。

脚本编写

1、配置信息填写

邮箱配置

这里我使用的qq邮箱作为发件账号,需要开启邮箱授权,获取授权码。

{
  host: "smtp.qq.com", // 主机
  secureConnection: true, // 使用 SSL
  port: 465, // SMTP 端口
  auth: {
    user: "jyeontu@qq.com", // 自己用于发送邮件的账号
    pass: "jyeontu", // 授权码(这个是假的,改成自己账号对应即可,获取方法: QQ邮箱-->设置-->账户-->POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV服务-->IMAP/SMTP开启 复制授权码)
  }
}
  • (1)打开pc端qq邮箱,点击设置,再点击帐户

  • (2)往下拉 可开启POP3/SMTP服务 根据提示即可获取qq邮箱授权码

  • (3)将获取到的授权码复制到配置信息里即可

数据库配置

填写数据库对应的配置信息。

{
  host: "localhost",
  user: "root", //数据库账号
  password: "jyeontu", //数据库密码
  database: "test", //数据库名称
}

gitee仓库配置

这里我们可以使用gitee来做一个免费的备份仓库,把备份数据推送到gitee上,这里需要填写一下gitee仓库的配置信息。

  • (1)新建私有数据库

因为我之前做了一个书签同步和tab标签保存的功能,数据也都放在这个仓库里,所以直接新建了一个目录sqlData用于保存数据库备份数据。

  • (2)配置ssh公钥 因为我们最后还需要将数据同步推送到远程仓库,不配置ssh认证的话,每次推送都需要输入密码,所以需要先配置下,配置好之后就可以不输密码,一键推送数据,不懂配置的同学可以Google一下,网上很多教程的,这里就不多说了。

  • (3)拉取仓库到本地并填写配置信息 创建好仓库后我们现在本地拉取一下,然后将仓库的本地地址和数据库存放目录填写到配置信息即可。

{
  giteePath: "E:/A_Gitee/jyeontu_info_storage", //用于保存数据的gitee仓库
  dir: "sqlData", //存放数据库数据的目录名
}

2、数据库数据导出

(1)引入模块

const mysql = require("mysql");
const fs = require("fs");
const archiver = require("archiver");
const { dbConfig } = require("./config.js");
  • mysql:用于连接和操作 MySQL 数据库。
  • fs:文件系统模块,用于文件操作,如写入文件。
  • archiver:用于创建压缩文件。
  • ./config.js引入dbConfig,获取数据库连接配置信息。

(2)连接数据库

const connection = mysql.createConnection(dbConfig);

function connectDatabase() {
  return new Promise((resolve) => {
    connection.connect((error) => {
      if (error) throw error;
      console.log("成功连接数据库!");
      resolve("成功连接数据库!");
    });
  });
}
  • 返回一个 Promise,在数据库连接成功时,打印“成功连接数据库!”并 resolve 一个成功连接的消息字符串。如果连接失败,抛出错误。

(3)导出表数据

function exportTableData(tableName) {
  return new Promise((resolve) => {
    connection.query(
      `SHOW CREATE TABLE ${tableName}`,
      (error, results, fields) => {
        if (error) throw error;
        const createTableStatement = results[0]["Create Table"];
        connection.query(
          `SELECT * FROM ${tableName}`,
          (error, results, fields) => {
            if (error) throw error;

            const insertStatements = results
              .map((row) => {
                const keys = [],
                  values = [];
                Object.entries(row).forEach((item) => {
                  keys.push(item[0]);
                  values.push(`'${item[1]}'`);
                });
                return `INSERT INTO ${tableName} (${keys.join(
                  ","
                )}) VALUES (${values.join(",")});`;
              })
              .join("\n");

            const sql = `${createTableStatement};\n\n${insertStatements}`;

            fs.writeFile(
              `${dbConfig.database}/${tableName}.sql`,
              sql,
              (err) => {
                if (err) throw err;
                console.log(`表${tableName} 数据已成功导出.`);
                resolve(`表${tableName} 数据已成功导出.`);
              }
            );
          }
        );
      }
    );
  });
}
  • 接受一个表名作为参数,返回一个 Promise。
  • 首先查询指定表的创建语句。
  • 然后查询表中的所有数据。
  • 将数据转换为插入语句格式,并与创建语句拼接成完整的 SQL 内容。
  • 将 SQL 内容写入以表名命名的.sql文件中,如果写入成功,打印表名和“数据已成功导出.”的消息并 resolve 相应消息字符串。如果写入失败,抛出错误。

(4)数据库查询

function mysqlQuery() {
  return new Promise((resolve) => {
    connection.query("SHOW TABLES", (error, results) => {
      if (error) throw error;
      resolve(results);
    });
  });
}
  • 返回一个 Promise,执行查询所有表名的 SQL 语句,将查询结果 resolve 出去。

(5)获取所有表数据

async function getAllTableData() {
  const reqList = [];
  const results = await mysqlQuery();
  results.forEach((result, index) => {
    const tableName = result[`Tables_in_${dbConfig.database}`];
    reqList.push(exportTableData(tableName));
  });
  await Promise.all(reqList);
  console.log("已全部导出!");
}
  • 首先调用mysqlQuery获取所有表名。
  • 遍历所有表名,对每个表名调用exportTableData函数,并将返回的 Promise 添加到reqList数组中。
  • 使用Promise.all等待所有表数据导出完成。

(6)打包成压缩文件

async function getZip() {
  return new Promise((resolve) => {
    const output = fs.createWriteStream(`${dbConfig.database}.zip`);
    const archive = archiver("zip", {
      zlib: { level: 9 }, // 设置压缩级别
    });
    output.on("close", function () {
      console.log("压缩完成," + archive.pointer() + " total bytes");
      resolve("finished");
    });
    archive.on("error", function (err) {
      throw err;
    });
    archive.pipe(output);
    // 要压缩的目录路径
    const sourceDir = dbConfig.database;
    archive.directory(sourceDir, false);
    archive.finalize();
  });
}
  • 使用fs.createWriteStream创建一个可写流output,用于将压缩后的数据写入一个以数据库名称命名的.zip文件中。
  • 使用archiver模块创建一个归档器archive,指定压缩格式为zip,并通过设置zlib选项的level属性为9来设置较高的压缩级别,以获得更好的压缩效果。
  • 为输出流output添加一个close事件监听器。当压缩完成,输出流关闭时,这个监听器会被触发。它会在控制台打印压缩完成的消息,并包含总共写入的字节数(通过archive.pointer()获取)。然后,调用resolve函数来通知 Promise 已成功完成,传递一个字符串"finished"作为成功的结果。
  • 为归档器archive添加一个error事件监听器。如果在压缩过程中发生错误,这个监听器会被触发,并抛出错误,以便在调用getZip函数的地方进行错误处理。
  • 使用archive.pipe(output)将归档器和输出流连接起来。这意味着归档器生成的压缩数据将被自动写入输出流。
  • 指定要压缩的目录路径为dbConfig.database所代表的目录。
  • 使用archive.directory(sourceDir, false)将指定目录添加到归档器中。第二个参数false表示不保留目录的完整路径在压缩包中(只包含目录内的内容)。
  • 最后,调用archive.finalize()开始压缩过程。这个方法会触发归档器开始将数据写入输出流,并在完成后触发输出流的close事件。

3、将数据库数据发送到邮箱

(1)引入模块和配置

const nodemailer = require("nodemailer");
const { mail, dbConfig } = require("./config.js");
  • 引入nodemailer模块,用于发送邮件。
  • ./config.js文件中引入maildbConfig对象,包含邮件发送的配置信息以及数据库相关的配置。

(2)创建邮件传输对象

const smtpTransport = nodemailer.createTransport(mail);
  • 使用nodemailer.createTransport方法创建一个邮件传输对象smtpTransport

(3)定义发送邮件函数

const sendMail = () => {
  return new Promise((resolve) => {
    //...
  });
};
  • 定义一个名为sendMail的函数,该函数返回一个 Promise,使得调用者可以异步等待邮件发送的结果。

(4)设置邮件选项

const mailOptions = {
  from: "1311395125@qq.com", // 发件人地址
  to: "jyeontu@163.com", // 收件人地址
  subject: "数据库备份数据", // 邮件主题
  text: dbConfig.database, // 邮件正文内容,这里可能是数据库的名称
  attachments: [
    {
      filename: dbConfig.database + ".zip", // 附件文件名
      path: dbConfig.database + ".zip", // 附件文件路径
    },
  ],
};
  • 创建一个mailOptions对象,设置邮件的发件人、收件人、主题、正文和附件。
  • 正文内容为从dbConfig中获取的数据库名称。
  • 附件设置为以数据库名称命名的.zip文件。

(5)发送邮件并处理结果

smtpTransport.sendMail(mailOptions, function (error, response) {
  if (error) {
    console.error("发送邮件失败:", error);
  } else {
    console.log("邮件发送成功");
  }
  smtpTransport.close(); // 发送完成关闭连接池
  resolve(true);
});
  • 使用smtpTransport.sendMail方法发送邮件,传入mailOptions和一个回调函数。
  • 在回调函数中,如果有错误,打印错误信息;如果发送成功,打印“邮件发送成功”。
  • 发送完成后,调用smtpTransport.close关闭邮件传输对象的连接池。
  • 最后,调用resolve(true)通知 Promise 已成功完成。

4、将数据库数据同步到gitee

以下是对这段代码的解释:

(1)引入模块和配置

const axios = require("axios");
const child_process = require("child_process");
const fs = require("fs");
const { giteeConfig, dbConfig } = require("./config");
  • 引入axios模块,用于发送 HTTP 请求。
  • 引入child_process模块,用于在 Node.js 中执行系统命令。
  • 引入fs模块,用于文件系统操作。
  • ./config文件中引入giteeConfigdbConfig对象,包含与 Gitee 相关的配置信息以及数据库配置信息。

(2)提取配置信息

const { giteePath, dir } = giteeConfig;
const sourceFilePath = dbConfig.database + ".zip";
const destinationFilePath = `${giteePath}/${dir}/${sourceFilePath}`;
  • giteeConfig中提取giteePath(Gitee 仓库的本地路径)和dir(存放数据库文件的目录)。
  • 确定源文件路径为数据库名称加上.zip后缀。
  • 计算目标文件路径,即 Gitee 仓库路径下特定目录中的源文件路径。

(3)同步最新数据

// 同步最新数据
console.log("正在同步最新数据……");
child_process.execSync(`cd ${giteePath} && git pull`);
console.log("已成功同步最新数据");
  • 使用child_process.execSync执行命令,切换到 Gitee 仓库路径并执行git pull命令,以同步远程仓库的最新数据。

(4)更新数据库文件数据

// 更新数据库文件数据
console.log("正在更新数据库文件数据……");
fs.copyFileSync(sourceFilePath, destinationFilePath);
console.log("已成功更新数据库文件数据");
  • 使用fs.copyFileSync同步方式将源文件(数据库的压缩文件)复制到目标文件路径(Gitee 仓库中的特定位置)。

(5)推送到远程数据库

// 推送到远程数据库
console.log("正在推送到远程数据库……");
child_process.execSync(`cd ${giteePath} && git add.`);
child_process.execSync(
  `cd ${giteePath} && git commit -m 更新数据库${dbConfig.database}数据`
);
child_process.execSync(`cd ${giteePath} && git push`);
console.log("已成功推送到远程数据库");
  • 使用child_process.execSync依次执行命令,切换到 Gitee 仓库路径,执行git add.将所有更改添加到暂存区,执行git commit提交更改并提供提交信息,执行git push将更改推送到远程仓库。

5、定时备份

如果需要定时备份数据库数据的话,可以使用corn编写一个定时任务来定时执行脚本即可。

  • * * * * * *分别对应:秒、分钟、小时、日、月、星期。
  • 每个字段可以是具体的值、范围、通配符(*表示每一个)或一些特殊的表达式。

例如:

0 0 * * *:每天午夜 0 点执行。
0 30 9 * * 1-5:周一到周五上午 9:30 执行。

你可以根据自己的需求设置合适的 cron 表达式来定时执行特定的任务。

测试

创建数据库

CREATE DATABASE test;

创建表

CREATE TABLE `t_user` (
  `id` int NOT NULL AUTO_INCREMENT,
  `name` varchar(255) NOT NULL,
  `age` int NOT NULL,
  PRIMARY KEY (`id`)
)

插入数据

INSERT INTO t_user (name,age) VALUES ('张三', 25);
INSERT INTO t_user (name,age) VALUES ('李四', 24);

导出数据

  • 本地导出数据

  • 邮件数据

  • gitee仓库数据

使用

1、源码下载

git clone https://gitee.com/zheng_yongtao/node-scripting-tool.git

2、依赖下载

npm install

3、配置数据填写

这里的配置信息需要修改为你自己的信息,数据库、gitee仓库、邮件配置。

4、脚本运行

node index.js

更多脚本

该脚本仓库里还有很多有趣的脚本工具,有兴趣的也可以看看其他的:gitee.com/zheng_yongt…


🌟觉得有帮助的可以点个star~

🖊有什么问题或错误可以指出,欢迎pr~

📬有什么想要实现的工具或想法可以联系我~


公众号

关注公众号『前端也能这么有趣』,获取更多有趣内容。

说在后面

🎉 这里是 JYeontu,现在是一名前端工程师,有空会刷刷算法题,平时喜欢打羽毛球 🏸 ,平时也喜欢写些东西,既为自己记录 📋,也希望可以对大家有那么一丢丢的帮助,写的不好望多多谅解 🙇,写错的地方望指出,定会认真改进 😊,偶尔也会在自己的公众号『前端也能这么有趣』发一些比较有趣的文章,有兴趣的也可以关注下。在此谢谢大家的支持,我们下文再见 🙌。