批量导入本地CSV至Google Sheets时顺序混乱,求LockService外的解决方法
本地多CSV批量导入Google Sheets的顺序问题
我正在开发无需先上传至Google Drive,直接将本地多个.csv文件批量导入Google Sheets的流程。目前已编写了如下代码,但遇到一个问题:最初未使用Promise时,导入的文件在表格中顺序混乱,原以为用Promise等待每个文件读取处理后再传入表格能解决问题,但仍存在同样的问题。除了已使用的LockService对象,请问有什么方法能让脚本等待每个部分完成导入后再进行下一步?
现有代码
index.html
<form> <input type="file" name="file" onchange="importCsv(this.parentNode)" accept=".csv,text/csv" multiple> </form> <p id="progress" style="display:none;"> <label for="upload" style="font-family: sans-serif;">Uploading...</label> <progress id="upload"></progress> </p> <script> function readFileAsBuffer(file){ return new Promise(function(resolve,reject){ let fr = new FileReader(); fr.onload = function(){ resolve(fr.result); }; fr.onerror = function(){ reject(fr); }; fr.readAsArrayBuffer(file); }); } async function importCsv(e) { document.getElementById("progress").style.display = "block"; const files = e.file.files; let fileReaders = []; let fileValues = []; for(let i = 0;i < files.length;i++){ fileReaders.push(readFileAsBuffer(files[i])); } await Promise.all(fileReaders).then((values) => { fileValues.push(values); }); for(let i=0; i < files.length; i++) { let file = files[i]; await google.script.run.withSuccessHandler(google.script.host.close).importCsv([[...new Int8Array(fileValues[0][i])], file.type, file.name]); } } </script>
script.gs
function onOpen() { SpreadsheetApp.getUi().createMenu("Import").addItem("Import CSVs", "importCsv").addToUi(); } function importCsv(e){ if (!e) { SpreadsheetApp.getUi().showModalDialog(HtmlService.createHtmlOutputFromFile("index"), "Import CSVs"); return; } const lock = LockService.getScriptLock(); lock.waitLock(30000); const csv = Utilities.parseCsv(Utilities.newBlob(...e).getDataAsString()); const sheet = SpreadsheetApp.getActiveSheet(); sheet.getRange(sheet.getLastRow() + 3, 1, csv.length, csv[0].length).setValues(csv); lock.releaseLock(); }
问题原因与解决方案
核心问题
google.script.run本身不返回Promise,直接用await无法等待服务器端函数执行完成。- 设置了
withSuccessHandler(google.script.host.close),导致第一个文件导入后直接关闭弹窗,后续文件的导入逻辑被中断。 - 即使弹窗没关闭,并发调用
google.script.run会导致服务器端请求无序处理,破坏导入顺序。
解决步骤
1. 封装google.script.run为Promise
在客户端脚本中添加工具函数,把服务器端调用包装成Promise,让await能真正等待请求完成:
function runServerFunction(funcName, ...args) { return new Promise((resolve, reject) => { google.script.run .withSuccessHandler(resolve) .withFailureHandler(reject)[funcName](...args); }); }
2. 修改客户端importCsv函数
调整逻辑,确保逐个处理文件,每个文件的导入请求完成后再处理下一个,最后统一关闭弹窗:
async function importCsv(e) { document.getElementById("progress").style.display = "block"; const files = Array.from(e.file.files); // 转换为数组方便操作 // 批量读取所有文件的Buffer(不影响顺序) const fileBuffers = await Promise.all(files.map(readFileAsBuffer)); // 按顺序逐个发送导入请求 for (let i = 0; i < files.length; i++) { const file = files[i]; const buffer = fileBuffers[i]; await runServerFunction("importCsv", [[...new Int8Array(buffer)], file.type, file.name]); } // 所有文件导入完成后关闭弹窗 google.script.host.close(); }
3. 服务器端代码(无需修改,可选优化)
现有服务器端的LockService已经能防止并发写入,现在因为是串行请求,sheet.getLastRow()的计算也会准确。如果需要更稳妥,可以在服务器端返回导入后的最后行号,客户端记录后传给下一次请求,但当前逻辑已经足够保证顺序。
这样修改后,脚本会严格按照用户选择文件的顺序,逐个完成导入,解决顺序混乱的问题。
内容的提问来源于stack exchange,提问作者Tim Schley
相关产品推荐
相关产品推荐

