Nodemon运行Google Drive转存S3脚本中途无报错崩溃如何解决
问题根因分析
- 首先
[nodemon] app crashed是开发工具nodemon的提示,掩盖了进程崩溃的真实原因,没有错误日志是因为你没有对Node.js全局异常和未处理Promise拒绝做捕获逻辑 - 内存溢出是最可能的崩溃原因:你当前的代码会一次性把所有待下载文件的内容以Buffer形式存到内存中等待批量上传,文件量过大或单个文件过大会直接占满8GB内存导致进程被系统杀掉
- 并发逻辑错误:
promises.push(await downloadFile(...))的写法会等待单个文件下载上传完成才处理下一个,同时所有文件的Buffer会堆积在内存中,完全没有利用并发能力还会加重内存负担 - 没有控制并发数:单页1000个文件的任务如果同时发起,会触发Google Drive API或S3的限流,报错未被捕获也会直接导致进程崩溃
解决方案
1. 先捕获真实错误日志
在脚本最开头添加全局异常监听代码,方便定位具体错误:
// 全局异常捕获 process.on('uncaughtException', (err) => { console.error('未捕获的异常:', err, err.stack) process.exit(1) }) process.on('unhandledRejection', (reason, promise) => { console.error('未处理的Promise拒绝:', reason, promise) process.exit(1) })
2. 修复并发逻辑,控制并发数
使用p-limit库控制同时执行的下载上传任务数,避免内存溢出和API限流:
- 先安装依赖:
npm i p-limit - 修改文件处理循环部分代码:
const pLimit = require('p-limit') // 调整为合适的并发数,建议5-10 const limit = pLimit(5) let promises = [] if (files.length) { const filesWithExt = files.filter(file => !!file.fileExtension); total = filesWithExt.length; console.log("文件总数", files.length); console.log("待迁移文件总数", filesWithExt.length); for (let i = 0; i < files.length; i++) { const file = files[i]; if (file.fileExtension) { const path = file.name; // 用limit包装异步任务控制并发 promises.push(limit(async () => { const exist = await s3_exist(`migration-temp-${stage}`, path); if (exist) { console.log(path, " 已存在于S3,跳过"); return { status: 'skipped', path } } return downloadFile(stage, file.id, path, auth) })) count = count + 1; console.log("..... 剩余 ", total - count, " 个"); } } await Promise.all(promises) // 存在下一页自动处理,无需手动输入pageToken if (res.data.nextPageToken) { console.log("开始处理下一页,令牌:", res.data.nextPageToken, "\n"); // 这里可以调用递归函数处理下一页,或者把pageToken存到变量循环处理 } process.exit(0) }
3. 不需要nohup也可以后台运行的方法
解决脚本需要输入参数但需要后台运行的问题:
- 正常用node启动脚本,不要用nodemon:
node index.js - 完成所有用户输入(环境选择、分页参数、谷歌授权等)后,按
Ctrl+Z暂停进程 - 输入
bg命令把暂停的进程切换到后台运行 - 输入
disown命令让进程脱离当前终端会话,关闭SSH连接后进程也会继续运行
4. 内存优化(可选,适合大文件场景)
修改下载上传逻辑为流式处理,文件内容不经过内存和本地磁盘,直接从Google Drive流传输到S3:
- 先在
helper.js中实现S3流式上传方法,使用AWS SDK的upload接口支持传入可读流作为Body - 修改
downloadFile函数:
const downloadFile = async (stage, fileId, finalName, auth) => { const drive = google.drive({ version: "v3", auth }); console.log(`正在处理 ${finalName}`); // 获取Google Drive下载流 const downloadStream = await drive.files.get( { fileId, alt: "media" }, { responseType: "stream" } ); // 直接将流上传到S3,不占用内存 return upload_to_s3_stream(downloadStream.data, finalName, `migration-temp-${stage}`) }
内容的提问来源于stack exchange,提问作者roshan poojary
相关产品推荐
相关产品推荐

