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

How to extract files from .tar archive with Google Apps Script

大家好,

我正在尝试从 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。

非常感谢任何帮助。

这个答案怎么样?不幸的是,在当前阶段,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 的 blob(tar 数据)被放入我的脚本和 运行 tarUnarchiver()。然后,每个文件都创建到文件夹中。

注:

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

限制:

使用此脚本时,有如下限制。这些限制是由于 Google 的规范。

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

参考:

在我的环境中,我可以确认脚本有效。但如果这个脚本不起作用,我深表歉意。届时,能否提供一个示例tar文件?我想检查它并修改脚本。