部署在GAE的Node.js服务器上传GCS遇只读文件系统错误求助
问题原因分析
Google App Engine(GAE)标准环境的文件系统大部分区域是只读的,仅实例的临时内存目录/tmp具备可写权限。你的代码中multer配置使用相对路径./tmp/uploads/testing/,会导致实际指向的目录不在GAE允许的可写区域内,或者创建子目录时因父目录只读而失败,最终触发EROFS: read-only file system错误。
解决方案
方案一:修正磁盘存储路径(兼容现有逻辑)
将multer的存储目录改为GAE允许的绝对路径/tmp,并确保子目录能被正确创建:
const fs = require('fs'); const multer = require('multer'); const testImageStorage = multer.diskStorage({ destination: function (req, file, cb) { // 使用GAE允许的绝对临时目录路径 const tempDir = '/tmp/uploads/testing/'; try { // 递归创建目录(不存在则自动创建) fs.mkdirSync(tempDir, { recursive: true }); cb(null, tempDir); } catch (error) { console.error('目录创建失败:', error); cb(error); } }, filename: function (req, file, cb) { try { cb(null, new Date().getTime() + file.originalname); } catch (error) { console.error('文件名生成失败:', error); cb(error); } }, }); const uploadTestImages = multer({ storage: testImageStorage, limits: { fileSize: 1024 * 1024 * 3 }, fileFilter: fileFilter, });
注意:GAE的
/tmp目录是实例级临时存储,实例销毁时数据会丢失,但仅用于临时缓存上传文件(上传至GCS后即可丢弃),完全满足需求。
方案二:使用内存存储(更优,避免磁盘IO)
直接在内存中处理上传文件,跳过磁盘写入步骤,彻底规避文件系统权限问题,同时提升性能:
1. 修改multer配置
const multer = require('multer'); // 改用内存存储,无需磁盘目录 const uploadTestImages = multer({ storage: multer.memoryStorage(), limits: { fileSize: 1024 * 1024 * 3 }, fileFilter: fileFilter, });
2. 调整路由处理逻辑
此时req.file将包含文件的Buffer数据,直接上传至GCS即可:
router.post( '/upload-to-gcp', uploadTestImages.single('testImage'), async (req, res) => { try { if (req.file) { const fileName = req.file.originalname; const destination = 'someFolderInBucket/' + fileName; try { // 直接上传Buffer到GCS const file = await storageService.uploadFromBuffer( req.file.buffer, destination, req.file.mimetype // 传入文件MIME类型,优化GCS存储元数据 ); const publicFile = await storageService.makeFilePublic(file); const publicUrl = storageService.getFilePublicUrl(publicFile); console.log(`文件公网地址: ${publicUrl}`); res.status(200).send('图片上传成功'); } catch (error) { console.error('GCS上传失败:', error); res.status(500).send('图片上传失败'); } } else { res.status(400).send('未上传任何文件'); } } catch (error) { console.error('请求处理错误:', error); res.status(500).send('服务器内部错误'); } } );
3. 新增GCS Buffer上传方法
在storageService中添加直接处理Buffer的上传函数:
// storageService.js const { Storage } = require('@google-cloud/storage'); const storage = new Storage(); async function uploadFromBuffer(buffer, destination, mimeType) { const bucket = storage.bucket('你的存储桶名称'); const file = bucket.file(destination); return new Promise((resolve, reject) => { const writeStream = file.createWriteStream({ metadata: { contentType: mimeType }, resumable: false }); writeStream.on('finish', () => resolve(file)); writeStream.on('error', reject); writeStream.end(buffer); }); } module.exports = { uploadFromBuffer, makeFilePublic, getFilePublicUrl };
方案选择建议
优先选择方案二:无磁盘IO操作,更适合无服务器环境的轻量化需求,同时彻底避免文件系统相关问题。
内容的提问来源于stack exchange,提问作者Tanzim Ahmed
相关产品推荐
相关产品推荐

