优化Google脚本-触发器不起作用

时间:2019-05-03 08:57:04

标签: google-apps-script

问题

根据我收到的错误电子邮件,我的Google脚本触发器未运行,因为它“占用大量CPU时间”。如何优化脚本以减少CPU时间? 我已经将sortGsheetFiles分成了不同的触发器,但是仍然要花费很多时间。它曾经与importXLSXtoGsheet函数结合使用。

脚本说明

我有52个文件夹,每个文件夹包含一个电子表格文件。
每个文件夹与不同的同事共享。 白天,人们会对文件进行更改。

  1. 最终,所有文件都收集到一个文件夹(gsheetFolder)中,并使用功能collectAndExportXLS转换为XLSX文件。

这些文件在晚上(使用批处理脚本和驱动器同步)复制到本地服务器,这将更新文件中的其他信息,然后复制回到importXLSXfolder

  1. 早晨importXLSXtoGsheet函数运行并将importXLSXfolder文件夹中的所有XLSX文件转换为gsheetFolder中的Gsheet文件。
  2. 运行sortGsheetFiles之后,将所有Gsheet文件排序并移动到52个文件夹之一中(使用当前电子表格中的数组列表)。

其他操作包括使用deleteFolder功能清理文件夹。

触发器

importXLSXtoGsheet-每天-早上6点至早上7点之间
sortGsheetFiles-每天-上午7点至8点之间
collectAndExportXLS-每天-晚上10点至晚上11点之间

脚本

var gsheetFolder = 'xxx';
var XLSXfolder = 'xxxxx';
var importXLSXfolder = 'xxxxx';

function checkEmptyFolder() {

var folders = DocsList.getAllFolders()
  for(n=0;n<folders.length;++n){
    if(folders[n].getFiles().length==0 && folders[n].getFolders().length==0){
     folders[n].setTrashed(true)
     Logger.log(folders[n].getName())
     }
   }  
}

function importXLSXtoGsheet(){

// ========= convert all XLS files in XLS folder to GSheet and put in the general gsheet folder - after that sort in gsheet filiaal folders =========
// cleanup exportXLS folder first 
  deleteFolder(XLSXfolder);

  var files = DriveApp.getFolderById(importXLSXfolder).searchFiles('title contains ".xlsx"');
  while(files.hasNext()) {
    var xFile = files.next();
    var name = xFile.getName();
    if (name.indexOf('.xlsx')) { 
      var ID = xFile.getId();
      var xBlob = xFile.getBlob();
      var newFile = {
        title : name + ('.xlsx'),
        key : ID,
        parents: [{"id": gsheetFolder}]
      }
      file = Drive.Files.insert(newFile, xBlob, {convert: true});
    }
  }
  deleteFolder(importXLSXfolder);
}

function sortGsheetFiles() {

  // ========= sort Gsheet folder and move to corresponding filiaal folders =========

  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var sheet = ss.getSheets()[0];
  var myArrayFileName = sheet.getRange("A2:A53").getValues();
  var myArrayFolderId = sheet.getRange("B2:B53").getValues();
  var a = myArrayFileName.join().split(',').filter(Boolean);
  var b = myArrayFolderId.join().split(',').filter(Boolean);

  var folderId = gsheetFolder;
  // Log the name of every file in the folder. 
  var files = DriveApp.getFolderById(folderId).getFiles();

     while (files.hasNext()) {
      var file = files.next();
        for (var i in a) {
          var id = file.getId();
          if (file.getName() == a[i]) { 
            moveFiles(id, b[i]); // Match found and move to corresponding folder
          }
        }
     }
  deleteFolder(importXLSXfolder);
}

function collectAndExportXLS() {
  // ========= collect all Gsheet files, copy to gsheet folder and convert to xlsx and move to xlsx folder =========

  // cleanup gsheet folder
  deleteFolder(gsheetFolder);

  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var sheet = ss.getSheets()[0];
  var myArrayFileName = sheet.getRange("A2:A53").getValues();
  var myArrayFolderId = sheet.getRange("B2:B53").getValues();
  var a = myArrayFileName.join().split(',').filter(Boolean);
  var b = myArrayFolderId.join().split(',').filter(Boolean);

  var folderId = gsheetFolder;

  for (var i in b) {
  var files = DriveApp.getFolderById(b[i]).getFiles();
    while (files.hasNext()) {
      var file = files.next();
      var id = file.getId();
      moveFiles(id , folderId);
    }
  }
  ConvertBackToXLS()
  deleteFolder(gsheetFolder);

}

function moveFiles(sourceFileId, targetFolderId) {
  var file = DriveApp.getFileById(sourceFileId);
  file.getParents().next().removeFile(file);
  DriveApp.getFolderById(targetFolderId).addFile(file);
}

function deleteFolder(folder) {
  //delete files in a folder without sending to trash!

  var eachFile, idToDLET, myFolder, rtrnFromDLET, thisFile, files;
  files = DriveApp.getFolderById(folder).getFiles();

  while (files.hasNext()) {//If there is another element in the iterator
    eachFile = files.next();
    idToDLET = eachFile.getId();
    //Logger.log('idToDLET: ' + idToDLET);

    rtrnFromDLET = Drive.Files.remove(idToDLET);
  };
   Logger.log('folder deleted');
}

function ConvertBackToXLS() {

  // Log the name of every file in the folder.
  var files = DriveApp.getFolderById(gsheetFolder).getFiles();
  var dir = DriveApp.getFolderById(XLSXfolder);
  while (files.hasNext()) {

     try {
     var file = files.next(); 
     var ss = SpreadsheetApp.openById(file.getId());
     Logger.log(file.getId());
     var url = "https://docs.google.com/feeds/download/spreadsheets/Export?key=" + file.getId() + "&exportFormat=xlsx";
     var params = {
      method      : "get",
      headers     : {"Authorization": "Bearer " + ScriptApp.getOAuthToken()},
      muteHttpExceptions: true
    };

    var blob = UrlFetchApp.fetch(url, params).getBlob();
    blob.setName(ss.getName());  
    var newfile = dir.createFile(blob); 

  } catch (f) {
    Logger.log(f.toString());
  }
 }
}

1 个答案:

答案 0 :(得分:1)

此修改如何?请认为这只是几个答案之一。

重要的一点是,请先根据实际情况测试脚本,然后再运行实际情况。

关于您的脚本:

  1. collectAndExportXLS():在此函数名称之前,我没有修改collectAndExportXLS()。因为我认为您可能在其他脚本或触发器上使用了此函数名称。

    1. 删除gsheetFolder中的所有文件。
    2. 将从活动电子表格中第一个索引的工作表的“ B2:B53”检索到的每个文件夹ID中的Google Spreadsheet转换为XLSX格式。
      • 文件名类似于sample.xlsx
      • 所有转换后的文件都放在XLSXfolder中。
    3. 删除所有文件夹ID中的所有文件。
    4. XLSXfolder中的XLSX文件由其他脚本放入importXLSXfolder中。
  2. importXLSXtoGsheet():在此函数名称之前,我没有修改collectAndExportXLS()。因为我认为您可能在其他脚本或触发器上使用了此函数名称。

    1. 删除XLSXfolder中的所有文件。
    2. importXLSXfolder中的所有XLSX文件转换为Google Spreadsheet。
      • 文件名类似于sample.xlsx
      • 已转换的Google电子表格放在gsheetFolder中。
    3. 删除importXLSXfolder中的所有文件。
  3. sortGsheetFiles()

    1. gsheetFolder中的Google Spreadsheets移动到从工作表“ B2:B53”中检索到的每个文件夹ID,该ID在活动Spreadsheet中为第一个索引。
      • 为了匹配文件夹ID,Google Spreadsheet的文件名以及从“ A2:A53”中检索的值。
    2. 删除importXLSXfolder中的所有文件。
  4. 我了解到,从您的问题来看,活动电子表格的“ A2:A53”列的文件名与Google电子表格的文件名相同,后者已放入“ B2”列的文件夹ID的文件夹中: B53”。

  5. 我知道所有文件的数量都少于100。

我理解如上。如果我的理解是正确的,那么该修改如何?在我的修改中,我使用了Drive API的Batch请求和类型为multipart/form-data的UrlFetchApp的fetchAll方法。批处理请求和fetchAll方法可以与异步过程一起使用。因此,我认为您的流程成本可能会降低。

为了使用这些方法,我使用了2个GAS库。在运行脚本之前,请为您的脚本安装这两个库。您可以按照以下步骤查看如何安装该库。

  1. Install a library for running the fetchAll method of UrlFetchApp with the type of multipart/form-data.
  2. Install a library for running batch request.

修改点:

  • collectAndExportXLS()

    • 每个文件夹中的文件ID由批处理请求检索。
    • 通过UrlFetchApp的fetchAll方法检索每个文件ID中的Blob(XLSX格式)。
    • XLSX格式的文件由FetchApp创建。
  • importXLSXtoGsheet()

    • 通过Drive API的files.list方法检索文件列表。
    • 通过批处理请求将XLSX格式的文件转换为Google Spreadsheet。
  • sortGsheetFiles()

    • 通过Drive API的files.list方法检索文件列表。
    • 使用批处理请求将Google Spreadsheet的文件移动到从活动Spreadsheet的“ B2:B53”列中检索到的每个文件夹ID。
  • deleteFolder()

    • 该文件夹中的文件被批处理请求删除。

当以上几点反映到您的脚本中时,它如下所示。

修改后的脚本:

安装2个库后,请运行以下脚本。

var gsheetFolder = '###';
var XLSXfolder = '###';
var importXLSXfolder = '###';

// Modified
function deleteFolder(folderId) {
  var url = "https://www.googleapis.com/drive/v3/files?q='" + folderId + "'+in+parents+and+trashed%3Dfalse&fields=files%2Fid&access_token=" + ScriptApp.getOAuthToken();
  var res = UrlFetchApp.fetch(url);
  var obj = JSON.parse(res.getContentText());
  var reqs = obj.files.map(function(e) {return {method: "DELETE", endpoint: "https://www.googleapis.com/drive/v3/files/" + e.id}});
  var requests = {batchPath: "batch/drive/v3", requests: reqs};
  if (requests.requests.length > 0) BatchRequest.Do(requests);
}

// Added
function deleteFiles(files) {
  var reqs = files.map(function(e) {return {method: "DELETE", endpoint: "https://www.googleapis.com/drive/v3/files/" + e.id}});
  var requests = {batchPath: "batch/drive/v3", requests: reqs};
  if (requests.requests.length > 0) BatchRequest.Do(requests);
}

// Added
function getValuesFromSpreadsheet() {
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var sheet = ss.getSheets()[0];
  return sheet.getRange("A2:B53").getValues();
}

// Modified
function sortGsheetFiles() {
  var url = "https://www.googleapis.com/drive/v3/files?q='" + gsheetFolder + "'+in+parents+and+mimeType%3D'" + MimeType.GOOGLE_SHEETS + "'+and+trashed%3Dfalse&fields=files(id%2Cname)&access_token=" + ScriptApp.getOAuthToken();
  var res = UrlFetchApp.fetch(url);
  var obj = JSON.parse(res.getContentText());
  var values = getValuesFromSpreadsheet();
  var reqs = values.reduce(function(ar, e) {
    for (var i = 0; i < obj.files.length; i++) {
      if (obj.files[i].name == e[0]) {
        ar.push({
          method: "PATCH",
          endpoint: "https://www.googleapis.com/drive/v3/files/" + obj.files[i].id + "?addParents=" + e[1] + "&removeParents=" + gsheetFolder,
        });
        break;
      }
    }
    return ar;
  }, []);
  var requests = {batchPath: "batch/drive/v3", requests: reqs};
  if (requests.requests.length > 0) BatchRequest.Do(requests);
  deleteFolder(importXLSXfolder);
}

// Modified
function importXLSXtoGsheet(){
  deleteFolder(XLSXfolder);
  var url = "https://www.googleapis.com/drive/v3/files?q='" + importXLSXfolder + "'+in+parents+and+mimeType%3D'" + MimeType.MICROSOFT_EXCEL + "'+and+trashed%3Dfalse&fields=files(id%2Cname)&access_token=" + ScriptApp.getOAuthToken();
  var res = UrlFetchApp.fetch(url);
  var obj = JSON.parse(res.getContentText());
  var reqs = obj.files.map(function(e) {return {
      method: "POST",
      endpoint: "https://www.googleapis.com/drive/v3/files/" + e.id + "/copy",
      requestBody: {mimeType: MimeType.GOOGLE_SHEETS, name: e.name + ".xlsx", parents: [gsheetFolder]},
    }
  });
  var requests = {batchPath: "batch/drive/v3", requests: reqs};
  if (requests.requests.length > 0) BatchRequest.Do(requests);
  deleteFolder(importXLSXfolder);
}

// Modified
function ConvertBackToXLS(fileList) {
  var token = ScriptApp.getOAuthToken();
  var reqs1 = fileList.map(function(e) {return {
      method: "GET",
      url: "https://docs.google.com/spreadsheets/export?id=" + e.id + "&exportFormat=xlsx&access_token=" + token,
    }
  });
  var res = UrlFetchApp.fetchAll(reqs1);
  var reqs2 = res.map(function(e, i) {
    var metadata = {name: fileList[i].name, parents: [XLSXfolder]};
    var form = FetchApp.createFormData(); // Create form data
    form.append("metadata", Utilities.newBlob(JSON.stringify(metadata), "application/json"));
    form.append("file", e.getBlob());
    var url = "https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart";
    return {url: url, method: "POST", headers: {Authorization: "Bearer " + token}, body: form};
  });
  FetchApp.fetchAll(reqs2);
}

// Modified
function collectAndExportXLS() {
  deleteFolder(gsheetFolder);
  var values = getValuesFromSpreadsheet();
  var reqs1 = values.reduce(function(ar, e) {
    if (e[0] && e[1]) {
      ar.push({
        method: "GET",
        endpoint: "https://www.googleapis.com/drive/v3/files?q='" + e[1] + "'+in+parents+and+trashed%3Dfalse&fields=files(id%2Cname)",
      });
    }
    return ar;
  }, []);
  var resForReq1 = BatchRequest.Do({batchPath: "batch/drive/v3", requests: reqs1});
  var temp = resForReq1.getContentText().split("--batch");
  var files = temp.slice(1, temp.length - 1).map(function(e) {return JSON.parse(e.match(/{[\S\s]+}/g)[0])});
  var fileList = files.reduce(function(ar, e) {return ar.concat(e.files.map(function(f) {return f}))}, []);
  ConvertBackToXLS(fileList);
  deleteFiles(fileList);
}

注意:

  • 在此修改中,未反映错误处理,因为我无法测试您的情况。因此,如果需要,请添加它。
  • 如果XLSX文件的文件大小很大,则可能会发生错误。

参考: