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ú
相关产品推荐
相关产品推荐

