Node.js项目在Render免费账户上传大文件至Google Drive内存不足问题咨询
解决Render免费账户Node.js上传大文件到Google Drive内存溢出问题
问题原因
当前代码使用multer.single()会将整个文件加载到内存中,399MB的视频文件加上Node.js运行时本身的内存占用,直接超过了Render免费账户512MB的内存限制,导致内存溢出报错。
解决方法
核心思路是避免将整个文件加载到内存,改用磁盘存储临时文件,并通过Google Drive的可恢复上传(Resumable Upload)流式传输文件。
1. 修改Multer配置为磁盘存储
替换原有的multer.single(),改用磁盘存储临时文件,避免内存占用过高:
const multer = require('multer'); const fs = require('fs'); const path = require('path'); // 确保临时目录存在,不存在则创建 const tmpDir = './tmp/uploads'; if (!fs.existsSync(tmpDir)) { fs.mkdirSync(tmpDir, { recursive: true }); } const storage = multer.diskStorage({ destination: (req, file, cb) => { cb(null, tmpDir); }, filename: (req, file, cb) => { // 添加时间戳避免文件名重复 cb(null, `${Date.now()}-${file.originalname}`); } }); const upload = multer({ storage: storage, limits: { fileSize: 500 * 1024 * 1024 } // 限制最大文件大小,比如500MB });
2. 改用Google Drive可恢复上传(流式传输)
修改uploadToGoogleDrive函数,直接读取磁盘文件流进行分片上传,无需将整个文件加载到内存:
const uploadToGoogleDrive = async (file, fid, auth) => { const idx = file.originalname.lastIndexOf('.'); const ext = idx === -1 ? '' : file.originalname.substring(idx); const fileMetadata = { name: `file${fid}${ext}`, parents: [googleFolderId], }; const drive = google.drive({ version: "v3", auth }); // 初始化可恢复上传会话 const resumableSession = await drive.files.create({ requestBody: fileMetadata, media: { mimeType: file.mimetype, body: fs.createReadStream(file.path) // 流式读取磁盘文件,不占内存 }, fields: "id, webViewLink", uploadType: 'resumable' }); // 执行上传并等待完成 const uploadResponse = await new Promise((resolve, reject) => { drive.files.create({ uploadType: 'resumable', resumable: resumableSession.resumable }, (err, res) => { if (err) reject(err); else resolve(res); }); }); // 设置公共读取权限 await drive.permissions.create({ fileId: uploadResponse.data.id, requestBody: { role: 'reader', type: 'anyone', } }); // 上传完成后删除临时文件,释放磁盘空间 fs.unlinkSync(file.path); return uploadResponse.data.webViewLink; };
3. 更新路由中间件
将原路由中的multer.single("file")替换为我们配置好的upload.single("file"):
app.post("/upload-file-to-google-drive2", upload.single("file"), async (req, res, next) => { if (!req.file) { res.status(400).send({ error: "No file uploaded." }); return; } const auth = authenticateGoogle(); const response = await uploadToGoogleDrive(req.file, req.body.id, auth); res.status(200).json({ response }); });
额外优化建议
- 添加定时任务清理超过24小时的临时文件,避免Render免费账户磁盘空间耗尽
- 给上传流程添加异常捕获,确保上传失败时临时文件也能被删除
- 监控Render账户的磁盘和内存使用情况,及时调整临时文件策略
内容的提问来源于stack exchange,提问作者Jens Stuber
相关产品推荐
相关产品推荐

