使用Node.js将Google Drive照片上传至AWS S3的异常问题咨询
核心根因
- Google Drive 公共文件的
webContentLink下载逻辑存在触发拦截的概率:当文件大小超过5MB时,Google Drive会自动插入病毒扫描提示页,你请求该链接时大概率返回的是HTML页面而非图片二进制流,直接上传到S3就会生成无效图片。时好时坏是因为小概率场景下Google没有触发扫描拦截,直接返回了文件内容。 - 你当前使用的下载逻辑没有处理跳转、认证逻辑:移除
&export=download后链接会产生302跳转,你用的request库默认没有开启自动跳转配置,即使开启跳转也没有携带Google接口的认证凭证,最终拿到的仍然是非图片内容。 - 循环逻辑存在变量作用域隐患:遍历代码中
for (file of fetchedFilesInFolder.files)没有给file加let/const声明,属于全局变量泄露,循环异步执行时很容易出现fileDetails和当前处理的文件不匹配的问题,进一步提升异常概率。
修复方案
方案1(最稳定,推荐):直接用Google Drive API拉取文件二进制,跳过网页链接下载
直接调用Drive API的files.get接口,指定alt: 'media'参数即可直接获取文件二进制流,完全绕开公共链接的拦截逻辑。
代码修改示例:
首先修改文件拉取逻辑,增加获取二进制的方法:
module.exports.fetchImageBinary = async (fileId) => new Promise(async (res, rej) => { const token = await googleUtil(); const drive = google.drive('v2'); drive.files.get( { auth: token, fileId: fileId, alt: 'media' // 关键参数,直接返回文件二进制 }, { responseType: 'arraybuffer' }, // 指定响应格式为二进制 (err, response) => { if (err) { console.log(`拉取文件二进制失败: ${err}`); return rej(err); } // 校验返回内容是否为图片 const contentType = response.headers['content-type']; if (!contentType.startsWith('image/')) { return rej(new Error(`返回内容非图片类型: ${contentType}`)); } res(Buffer.from(response.data)); }, ); });
然后修改遍历和上传逻辑,删除原有的公共链接下载步骤:
// 遍历增加let声明,修复作用域问题 for (let file of fetchedFilesInFolder.files) { try { // 直接拉取二进制 const imageBuffer = await fetchImageBinary(file.id); // 直接上传S3,不需要再调用request下载 const uploadResult = await s3.upload({ Bucket: process.env.MEDIA_S3_BUCKET, Key: path, // 替换为你实际的文件路径 Body: imageBuffer, ContentType: file.mimeType // 建议指定正确的Content-Type,方便S3后续访问 }).promise(); } catch (e) { // 可自行增加失败重试逻辑,比如失败后重试2-3次 console.error(`文件${file.id}上传失败:`, e); } }
方案2(兼容原有逻辑,不推荐):调整公共链接下载配置
如果坚持用webContentLink下载,需要修改request配置,携带认证信息、开启自动跳转并增加内容校验:
const token = await googleUtil(); const options = { uri: url, encoding: null, followAllRedirects: true, // 开启自动跟随跳转 headers: { 'Authorization': `Bearer ${token}` // 携带Google认证头,跳过扫描提示页 } }; const body = await request(options); // 增加内容校验,避免把HTML页面存入S3 const isImage = Buffer.isBuffer(body) && body.length > 1024 * 1024; // 校验文件大小大于1MB if (!isImage) { throw new Error('下载内容非有效图片'); }
内容的提问来源于stack exchange,提问作者ujjwal
相关产品推荐
相关产品推荐

