如何用AppsScript结合Promise上传文件至Drive并获取有效文件ID
问题分析与解决方案
代码中的核心错误
- Promise链逻辑错误:
sendFilewithPromise里,Promise已经resolve(file.getId())(即返回的是字符串类型的文件ID),但后续又调用了then(res => res.getId())——字符串没有getId()方法,这会直接触发catch分支,导致返回错误对象而非预期的文件ID。 - 函数调用不匹配:
setupFileUrl中调用的是sendFile(),但你提供的异步函数是sendFilewithPromise,参数类型也不统一(前者接收Blob,后者接收base64字符串)。 - GAS异步特性误解:Google Apps Script中
DriveApp.createFile()本身是同步阻塞的,上传完成后才会返回文件对象,你遇到的生产环境错误大概率不是因为异步未完成,而是权限、资源或参数问题。
修正后的代码方案
方案1:简化为同步实现(更适合GAS环境)
因为DriveApp操作是同步的,无需额外用Promise,直接逻辑即可:
// 同步上传文件到Drive function sendFile(blob) { try { const folder = DriveApp.getFolderById(driveFolderID); return folder.createFile(blob); } catch (e) { const logSheet = SpreadsheetApp.openById(appSpreadsheetID).getSheetByName("log"); logSheet.appendRow([new Date(), user, e, "sendFile: 文件上传失败"]); throw e; // 抛出错误让调用者处理,而非返回错误对象 } } // 生成文件URL和ID function setupFileUrl(fileContent, fileName) { try { const contentType = fileContent.substring(5, fileContent.indexOf(';')); const bytes = Utilities.base64Decode(fileContent.substr(fileContent.indexOf('base64,') + 7)); const fileBlob = Utilities.newBlob(bytes, contentType, fileName); const addedFile = sendFile(fileBlob); return [addedFile.getUrl(), addedFile.getId()]; } catch (e) { const logSheet = SpreadsheetApp.openById(appSpreadsheetID).getSheetByName("log"); logSheet.appendRow([new Date(), user, e, "setupFileUrl: 处理流程失败"]); throw e; } }
方案2:修正Promise实现(若必须用异步)
如果确实需要Promise,需修正链逻辑:
function sendFilewithPromise(base64Data, nomeArq) { const splitBase = base64Data.split(','); const type = splitBase[0].split(';')[0].replace('data:', ''); const byteCharacters = Utilities.base64Decode(splitBase[1]); const ss = Utilities.newBlob(byteCharacters, type, nomeArq); return new Promise((resolve, reject) => { try { const folder = DriveApp.getFolderById(driveFolderID); const file = folder.createFile(ss); resolve(file.getId()); // 直接返回文件ID字符串 } catch (error) { reject(error); } }).catch((e) => { const logSheet = SpreadsheetApp.openById(appSpreadsheetID).getSheetByName("log"); logSheet.appendRow([new Date(), user, e, "sendFilewithPromise: 上传失败"]); throw e; // 抛出错误,避免返回错误对象 }); } // 调用异步函数需用async/await async function setupFileUrlAsync(fileContent, fileName) { try { const contentType = fileContent.substring(5, fileContent.indexOf(';')); const bytes = Utilities.base64Decode(fileContent.substr(fileContent.indexOf('base64,') + 7)); // 转换为sendFilewithPromise需要的base64格式 const base64Data = `data:${contentType};base64,${Utilities.base64Encode(bytes)}`; const fileId = await sendFilewithPromise(base64Data, fileName); const uploadedFile = DriveApp.getFileById(fileId); return [uploadedFile.getUrl(), fileId]; } catch (e) { const logSheet = SpreadsheetApp.openById(appSpreadsheetID).getSheetByName("log"); logSheet.appendRow([new Date(), user, e, "setupFileUrlAsync: 处理流程失败"]); throw e; } }
生产环境错误排查步骤
- 权限验证:
- 确认部署GAS时的权限设置:进入「部署」→「管理部署」→「编辑」,确保权限范围覆盖Drive操作,且部署账号对目标文件夹
driveFolderID有写入权限。
- 确认部署GAS时的权限设置:进入「部署」→「管理部署」→「编辑」,确保权限范围覆盖Drive操作,且部署账号对目标文件夹
- 参数有效性:
- 验证
driveFolderID和appSpreadsheetID是否正确,可直接在Drive/Sheets中复制ID测试。
- 验证
- 资源限制:
- 检查文件大小:DriveApp单文件上传上限为50MB,超过需改用Drive API批量上传。
- 检查GAS执行时间:单次脚本执行最长6分钟,大文件上传可能超时,需拆分或优化。
- 日志优化:
- 避免频繁打开Spreadsheet写入日志,可先将日志缓存到数组,批量写入,减少服务器请求开销。
内容的提问来源于stack exchange,提问作者Guilherme Lage
相关产品推荐
相关产品推荐

