You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

批量导入本地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();
}

问题原因与解决方案

核心问题

  1. google.script.run本身不返回Promise,直接用await无法等待服务器端函数执行完成。
  2. 设置了withSuccessHandler(google.script.host.close),导致第一个文件导入后直接关闭弹窗,后续文件的导入逻辑被中断。
  3. 即使弹窗没关闭,并发调用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 09:22:18