如何使用Google Apps脚本从.tar存档中提取文件

时间:2019-02-24 08:17:32

标签: google-apps-script tar

大家好,

我正在尝试从Gmail获取tar.gz附件,提取文件并将其保存到Google云端硬盘。这是每日自动生成的报告,由于原始大小> 25mb而被压缩。

到目前为止,我已经知道了:

  var sheet   = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Setup");

  var gmailLabels  = sheet.getRange("B2:B2").getValue();  //I have my Gmail Label stored here
  var driveFolder  = sheet.getRange("B5:B5").getValue();  //I have my GDrive folder name stored here

  // apply label filter, search only last 24hrs mail
  var filter = "has:attachment label:" + gmailLabels + " after:" + Utilities.formatDate(new Date(new Date().getTime()-1*(24*60*60*1000)), "GMT", "yyyy/MM/dd");

  var threads = GmailApp.search(filter, 0, 1); // check only 1 email at a time  

  var folder = DriveApp.getFoldersByName(driveFolder);

  if (folder.hasNext()) {
    folder = folder.next();
  } else {
    folder = DriveApp.createFolder(driveFolder);
  }


    var message = threads[0].getMessages()[0];

    var desc   = message.getSubject() + " #" + message.getId();
    var att    = message.getAttachments();

    for (var z=0; z<att.length; z++) {
      var attName = att[z].getName()
      var attExt = attName.search('csv')
      if (attExt > 0){ var fileType = "csv"; }
      else {
        var attExt = attName.search('tar.gz');
        if (attExt > 0){ var fileType = "gzip"; }
        else {
          threads[x].addLabel(skipLabel);  
          continue;
        }
      }

      // save the file to GDrive
      try {
        file = folder.createFile(att[z]);
        file.setDescription(desc);
      }
      catch (e) {
        Logger.log(e.toString());
      }

      // extract if gzip
      if (fileType == 'gzip' ){
        var ungzippedFile = Utilities.ungzip(file);
        try {
          gz_file = folder.createFile(ungzippedFile);
          gz_file.setDescription(desc);
        }
        catch (e) {
          Logger.log(e.toString());
        }
      }

    }

一切正常,但在最后一步中,它仅解压缩了.gz文件并将.tar文件保存在云端硬盘中。接下来我该怎么办? .tar文件包含一个.csv文件,之后需要提取并处理。

我应该补充一点,我仅限于使用GAS。

任何帮助都表示赞赏。

1 个答案:

答案 0 :(得分:4)

这个答案怎么样?不幸的是,在当前阶段,尚无方法可从Google Apps脚本中的tar文件中提取文件。但是幸运的是,我们可以从wiki of tar中检索tar数据的结构。我使用此结构数据通过Google Apps脚本实现了此方法。

1。取消存档tar数据:

在运行此脚本之前,请将tar文件的文件ID设置为run()。然后,运行run()

示例脚本:

function tarUnarchiver(blob) {
  var mimeType = blob.getContentType();
  if (!mimeType || !~mimeType.indexOf("application/x-tar")) {
    throw new Error("Inputted blob is not mimeType of tar. mimeType of inputted blob is " + mimeType);
  }
  var baseChunkSize = 512;
  var byte = blob.getBytes();
  var res = [];
  do {
    var headers = [];
    do {
      var chunk = byte.splice(0, baseChunkSize);
      var headerStruct = {
        filePath: function(b) {
          var r = [];
          for (var i = b.length - 1; i >= 0; i--) {
            if (b[i] != 0) {
              r = b.slice(0, i + 1);
              break;
            }
          }
          return r;
        }(chunk.slice(0, 100)),
        fileSize: chunk.slice(124, 124 + 11),
        fileType: Utilities.newBlob(chunk.slice(156, 156 + 1)).getDataAsString(),
      };
      Object.keys(headerStruct).forEach(function(e) {
        var t = Utilities.newBlob(headerStruct[e]).getDataAsString();
        if (e == "fileSize") t = parseInt(t, 8);
        headerStruct[e] = t;
      });
      headers.push(headerStruct);
    } while (headerStruct.fileType == "5");
    var lastHeader = headers[headers.length - 1];
    var filePath = lastHeader.filePath.split("/");
    var blob = Utilities.newBlob(byte.splice(0, lastHeader.fileSize)).setName(filePath[filePath.length - 1]).setContentTypeFromExtension();
    byte.splice(0, Math.ceil(lastHeader.fileSize / baseChunkSize) * baseChunkSize - lastHeader.fileSize);
    res.push({fileInf: lastHeader, file: blob});
  } while (byte[0] != 0);
  return res;
}

// Following function is a sample script for using tarUnarchiver().
// Please modify this to your situation.
function run() {
  // When you want to extract the files from .tar.gz file, please use the following script.
  var id = "### file ID of .tar.gz file ###";
  var gz = DriveApp.getFileById(id).getBlob().setContentTypeFromExtension();
  var blob = Utilities.ungzip(gz).setContentTypeFromExtension();

  // When you want to extract the files from .tar file, please use the following script.
  var id = "### file ID of .tar file ###";
  var blob = DriveApp.getFileById(id).getBlob().setContentType("application/x-tar");

  // Extract files from a tar data.
  var res = tarUnarchiver(blob);

  // If you want to create the extracted files to Google Drive, please use the following script.
  res.forEach(function(e) {
    DriveApp.createFile(e.file);
  });

  // You can see the file information by below script.
  Logger.log(res);
}

2。修改脚本:

例如,如果此脚本用于您的脚本,该如何处理?使用以上脚本的tarUnarchiver()。但是我不确定您如何使用此脚本。因此,请将此作为示例。

示例脚本:

// extract if gzip
if (fileType == 'gzip' ){
  var ungzippedFile = Utilities.ungzip(file);
  try {

    var blob = ungzippedFile.setContentType("application/x-tar"); // Added
    tarUnarchiver(blob).forEach(function(e) {folder.createFile(e.file)}); // Added

  }
  catch (e) {
    Logger.log(e.toString());
  }
}
  • 在此修改后的脚本中,将ungzippedFile(tar数据)的blob放入我的脚本并运行tarUnarchiver()。然后,将每个文件创建到该文件夹​​。

注意:

  • 运行此脚本时,如果发生与mimeType相关的错误,请将“ tar”的mimeType设置为输入blob。
    • 作为设置mimeType的方法,可以使用以下方法。
      • blob.setContentTypeFromExtension() Ref
      • blob.setContentType("application/x-tar") Ref
    • 可能已经在blob中获得了mimeType。当时不需要setContentTypeFromExtension()setContentType()
  • 如果要检索每个文件的文件路径,请检查来自tarUnarchiver()的响应。您可以从响应中将其视为fileInf的属性。

限制:

使用此脚本时,存在以下限制。这些限制是由于Google的规范所致。

  • 关于文件大小,当tar数据的大小超过50 MB(52,428,800字节)时,发生与大小限制有关的错误。
  • 当提取的文件大小超过50 MB时,会发生错误。
  • 当提取的文件的单个文件大小接近50 MB时,可能会发生错误。
    • 在我的环境中,我可以确认可以提取49 MB的大小。但是在只有50 MB的情况下 ,发生了错误。

参考:

在我的环境中,我可以确认该脚本有效。但是,如果该脚本不起作用,我深表歉意。那时,您可以提供示例tar文件吗?我想检查一下并修改脚本。