如何使用Google Apps Script从.tar归档文件中提取文件

3

大家好,

我正在尝试从Gmail获取一个tar.gz附件,解压文件并将其保存到Google Drive。这是我每天获得的自动生成报告,由于原始大小大于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 文件保存到 Drive 中。接下来该怎么办?.tar 文件包含一个我需要提取和处理的 .csv 文件。

我应该补充说明,我仅限于使用 GAS。

非常感谢任何帮助。


你尝试过使用 utilities 方法,例如 ungzip(blob) 吗? - MαπμQμαπkγVπ.0
是的,从底部数第11行代码调用了Utilities.ungzip()。它可以正确地提取.gz文件,但在解压缩后留下了一个.tr归档文件,我还没有找到处理它的方法,因此有这个问题。 - ErrHuman
1个回答

4
这个答案怎么样?很遗憾,目前在Google Apps Script中没有提取tar文件的方法。但是,我们可以从tar百科中获取tar数据的结构。我使用这个结构数据,在Google Apps Script中实现了这种方法。

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数据)放到我的脚本中并执行tarUnarchiver()。然后,每个文件都会创建到文件夹中。

注意:

  • 运行此脚本时,如果出现与mimeType相关的错误,请将“tar”的mimeType设置为输入blob。
    • 设置mimeType的方法如下:
    • 可能已经在blob中获取了mimeType。在那种情况下,不需要使用setContentTypeFromExtension()setContentType()
  • 如果您想检索每个文件的文件路径,请检查tarUnarchiver()的响应。您可以将其视为响应的fileInf属性。

限制:

当使用此脚本时,存在以下限制。这些限制是由Google的规范造成的。

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

参考文献:

在我的环境中,我可以确认该脚本有效。但如果此脚本无效,我很抱歉。那时,您可以提供一个tar文件示例吗?我想检查它并修改脚本。


非常感谢您提供的详细答案。我会在今天下午进行测试,并回复您确认结果。真的非常感激! - ErrHuman
刚刚测试了一下,非常好用,非常感谢您抽出时间编写并分享这个函数! - ErrHuman
@ErrHuman,我很高兴你的问题得到了解决。我从你的问题中学到了东西。也谢谢你。 - Tanaike

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接