You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Node.js实现文件夹自动压缩发送Telegram故障排查及代码修复

目录监控备份Telegram未收到文件:问题排查与修复

我需要用Node.js实现以下功能:监控一个包含多级子文件夹和文件的目录,当目录内出现文件新增、修改或删除操作时,自动将整个目录压缩为ZIP包并发送到我的Telegram频道。但运行下方代码后,并未收到发送的压缩文件,请帮忙排查并修复代码问题。

原代码

const TelegramBot = require('node-telegram-bot-api');
const chokidar = require('chokidar');
const fs = require('fs');
const archiver = require('archiver');
const moment = require('moment');

// Configuration Information
const telegramBotToken = 'YOUR_TELEGRAM_BOT_TOKEN';
const chatId = 'YOUR_CHAT_ID'; // ID of the channel or group you want to send to
const baseFolder = 'path_to_your_folder'; // The folder you want to monitor

const bot = new TelegramBot(telegramBotToken, { polling: true });

let isProcessing = false; // Variable to track if processing is currently in progress

const sendMessage = (message) => {
  bot.sendMessage(chatId, message);
};

const createZipArchive = async () => {
  if (isProcessing) {
    return; // Another processing is already in progress
  }

  isProcessing = true; // Start the processing
  const timestamp = moment().format('YYYY-MM-DD HH:mm:ss');
  const zipFileName = `backup_${timestamp}.zip`;

  const output = fs.createWriteStream(zipFileName);
  const archive = archiver('zip', { zlib: { level: 9 } });

  output.on('close', () => {
    sendMessage(`📁 Folder backup created at: ${timestamp}`);
    bot.sendDocument(chatId, zipFileName);
    fs.unlink(zipFileName, (err) => {
      if (err) {
        console.error(`Error deleting ZIP file: ${err}`);
      }
      isProcessing = false; // End the processing
    });
  });

  archive.pipe(output);
  archive.directory(baseFolder, false);
  await archive.finalize();
};

// Use chokidar to monitor changes in the folder
chokidar.watch(baseFolder, { ignored: /[\/\\]\./ }).on('all', (event, path) => {
  if (event === 'add' || event === 'change' || event === 'unlink') {
    createZipArchive();
  }
});

console.log('Bot is running and watching for changes...');

核心问题排查

  • 错误处理缺失:原代码未监听archiver的error事件,若压缩过程中出现权限不足、文件被占用等错误,output的close事件不会触发,导致isProcessing一直为true,后续任务无法执行,也看不到错误日志。
  • Telegram Bot模式错误:使用polling: true会让bot持续轮询Telegram服务器,此处仅需主动发送文件,不需要轮询,浪费资源且可能干扰发送流程。
  • 异步操作未处理错误:bot.sendMessage、bot.sendDocument均返回Promise,原代码未捕获错误,发送失败时无法排查原因。
  • 文件名含空格:生成的ZIP文件名包含空格,可能导致Telegram API无法正确识别文件路径。
  • 频繁触发备份:短时间内多次修改文件会重复调用createZipArchive,虽有isProcessing拦截,但缺少防抖机制,易触发不必要的重复压缩。
  • 目录路径问题:若baseFolder使用相对路径,可能因进程启动目录不同导致找不到目标目录。

修复后的完整代码

const TelegramBot = require('node-telegram-bot-api');
const chokidar = require('chokidar');
const fs = require('fs');
const path = require('path');
const archiver = require('archiver');
const moment = require('moment');

// 配置信息
const telegramBotToken = 'YOUR_TELEGRAM_BOT_TOKEN';
const chatId = 'YOUR_CHAT_ID'; // 目标频道/群组ID
const baseFolder = path.resolve('path_to_your_folder'); // 转换为绝对路径

// 初始化Bot,关闭polling模式
const bot = new TelegramBot(telegramBotToken);

let isProcessing = false;
let debounceTimer = null;

// 封装带错误处理的消息发送函数
const sendMessage = async (message) => {
  try {
    await bot.sendMessage(chatId, message);
  } catch (err) {
    console.error('发送消息失败:', err);
  }
};

const createZipArchive = async () => {
  if (isProcessing) return;
  isProcessing = true;

  const timestamp = moment().format('YYYY-MM-DD_HH-mm-ss'); // 用下划线替代空格
  const zipFileName = `backup_${timestamp}.zip`;
  const output = fs.createWriteStream(zipFileName);
  const archive = archiver('zip', { zlib: { level: 9 } });

  // 监听压缩错误
  archive.on('error', (err) => {
    console.error('压缩文件失败:', err);
    sendMessage('⚠️ 备份失败:压缩过程出错');
    fs.unlinkSync(zipFileName, (unlinkErr) => {
      if (unlinkErr) console.error('删除损坏的ZIP文件失败:', unlinkErr);
    });
    isProcessing = false;
  });

  output.on('close', async () => {
    try {
      await sendMessage(`📁 文件夹备份已创建:${timestamp}`);
      await bot.sendDocument(chatId, zipFileName);
      console.log('备份文件已发送到Telegram');
    } catch (err) {
      console.error('发送文件失败:', err);
      sendMessage('⚠️ 备份失败:无法发送到Telegram');
    } finally {
      // 无论发送是否成功,都删除本地ZIP文件
      fs.unlink(zipFileName, (err) => {
        if (err) console.error('删除ZIP文件失败:', err);
        isProcessing = false;
      });
    }
  });

  try {
    archive.pipe(output);
    archive.directory(baseFolder, false);
    await archive.finalize();
  } catch (err) {
    console.error('压缩初始化失败:', err);
    isProcessing = false;
  }
};

// 添加防抖机制,避免短时间内多次触发
const debouncedCreateZip = () => {
  clearTimeout(debounceTimer);
  debounceTimer = setTimeout(() => {
    createZipArchive();
  }, 3000); // 3秒防抖,可根据需求调整
};

// 监控目录变化
chokidar.watch(baseFolder, { ignored: /[\/\\]\./ }).on('all', (event, path) => {
  if (['add', 'change', 'unlink'].includes(event)) {
    debouncedCreateZip();
  }
});

console.log('Bot已启动,正在监控目录变化...');

额外注意事项

  • 确保Telegram Bot已被添加到目标频道并授予发送消息和文件的权限。
  • 验证chatId是否正确:频道ID通常以-100开头,可通过@userinfobot获取。
  • 检查Node.js版本是否兼容依赖包,建议使用LTS版本。
  • 确保运行进程对baseFolder有读写权限。

内容的提问来源于stack exchange,提问作者Nguyễn Xuân Phú

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 11:47:13