Google表单关联脚本随机生成重复文件夹问题咨询
Google Apps Script 文件夹重复创建问题排查与解决
问题描述
为Google表单编写的脚本,会在用户提交并上传文件后,根据回答将文件移动到层级文件夹(路径:第一题答案/第二题答案/第三题答案/文件)。若对应层级文件夹不存在,通过getSubFolder_函数创建。目前脚本偶发随机问题:
- 特定子文件夹不存在时,有时会生成同名重复文件夹(可出现在任意层级)
- 若第一层级文件夹重复,脚本会在两个同名文件夹下都创建后续子文件夹,但仅将文件移入最后创建的文件夹
核心代码
const onFormSubmit = ({ response } = {}) => { try { const firstItemAnswer = response.getItemResponses()[0].getResponse() const secondItemAnswerfull = response.getItemResponses()[1].getResponse(); const secondItemAnswer = secondItemAnswerfull.split(' ')[0]; const thirdItemAnswer = response.getItemResponses()[2].getResponse() ; const files = response .getItemResponses() .filter( (itemResponse) => itemResponse.getItem().getType().toString() === "FILE_UPLOAD" ) .map((itemResponse) => itemResponse.getResponse()) .reduce((a, b) => [...a, ...b], []); if (files.length > 0) { const parentFolder = DriveApp.getFolderById(PARENT_FOLDER_ID); const newparent1 = getSubFolder_(parentFolder, firstItemAnswer); const newparent2 = getSubFolder_(newparent1, secondItemAnswer); const newparent3 = getSubFolder_(newparent2, thirdItemAnswer); files.forEach((fileId) => { // Move each file into the custom folder DriveApp.getFileById(fileId).moveTo(newparent3); DriveApp.getFileById(fileId).setName(firstItemAnswer + "_" + secondItemAnswer + "_Month_" + thirdItemAnswer); }); } } catch (f) { Logger.log(f); } }; //function to create folder if not exist function getSubFolder_(objParentFolder, subFolderName) { const subFolders = objParentFolder.getFolders(); while (subFolders.hasNext()) { let folder = subFolders.next(); if (folder.getName() === subFolderName) { return folder; } } return objParentFolder.createFolder(subFolderName); }
问题解答
1. 重复文件夹的成因与解决方法
成因
问题根源是并发竞态条件:当多个用户几乎同时提交表单时,onFormSubmit触发器会同时启动多个脚本实例。假设实例A正在检查某层级文件夹是否存在,还未完成创建时,实例B也进行同样的检查——此时实例A创建的文件夹还未同步到Drive的查询结果中,实例B会判定文件夹不存在,进而创建另一个同名文件夹。
解决方法
使用Google Apps Script的LockService添加互斥锁,确保同一时间只有一个脚本实例执行文件夹的检查与创建逻辑,避免竞态:
// 更新后的getSubFolder_函数,加入锁机制 function getSubFolder_(objParentFolder, subFolderName) { const lock = LockService.getScriptLock(); // 等待最多30秒获取锁,超时则抛出错误(可根据需求调整时间) lock.waitLock(30000); try { // 用getFoldersByName直接查询,替代遍历所有子文件夹,提升效率 const subFolders = objParentFolder.getFoldersByName(subFolderName); if (subFolders.hasNext()) { return subFolders.next(); } return objParentFolder.createFolder(subFolderName); } finally { // 无论是否成功,都释放锁让其他实例执行 lock.releaseLock(); } }
2. 第一层级重复时的子文件夹创建问题及解决方案
原因
第一层级的重复文件夹本身就是并发创建的产物:两个同时执行的脚本实例各自完成了“检查-创建”流程,生成了同名的第一层级文件夹,随后各自在自己创建的文件夹下生成第二、第三层级子文件夹。你看到“仅移入最后创建的那个”,大概率是因为其中一个实例的执行完成时间更晚,或者查看时只关注到了最新创建的文件夹。
是否返回文件夹ID能解决?
不能。问题的核心是检查文件夹是否存在与创建文件夹的操作不是原子性的,无论返回文件夹对象还是ID,只要这个过程没有互斥锁保护,竞态条件依然存在,重复创建的问题无法避免。只有通过锁机制确保检查与创建的原子性,才能彻底解决该问题。
内容的提问来源于stack exchange,提问作者Moorli
相关产品推荐
相关产品推荐

