将多个PDF合并成一个PDF

7

我在处理代码时遇到了一些问题。我试图循环遍历一个包含许多PDF文件的Drive文件夹,然后将这些文件合并成一个文件。当我使用我的代码时,它只会创建一个包含Drive文件夹中最后一个PDF文件的PDF文件,并没有像预期的那样将它们全部合并在一起。

function MergeFiles(){
  var folder = DocsList.getFolderById('myFolderID'); 
  var files = folder.getFiles(); 
  var blobs = [];    
  for( var i in files )   
    blobs.push(files[i].getBlob().getBytes());
  Logger.log(blobs.push(files[i].getBlob().getBytes()));
  var myPDF = Utilities.newBlob(blobs.pop(), "application/pdf", "newPDF.pdf");
  folder.createFile(myPDF);
}

2
自2011年7月以来,已经有一个名为[Issue 699](http://code.google.com/p/google-apps-script-issues/issues/detail?id=699)的请求谷歌应用脚本API“拆分和合并PDF页面”。 - Mogsdad
有趣...也许现在还做不到。??? - user1518316
你看过这个应用程序吗?它不是脚本,而是一个很好的驱动器实用工具 :-) - Serge insas
7个回答

13

这不仅仅是简单地合并每个文件的数据。每个文件的实际可用数据都与标记和其他代码打包在一起(类似于HTML和其他文档格式)。您需要解码每个PDF文件,组合必要的部分,然后使用新的“打包”重新编码。这需要对PDF规范和结构有工作知识,可以从Adobe 这里免费获取。

我利用这些信息编写了一个满足我的需求的脚本。但是,它不能考虑到每种可能性,因此,特别是合并任何需要PDF-1.4及更高版本的文档时,需要进行相当多的工作。

/**
 * Merges all given PDF files into one.
 *
 * @param {Folder} directory the folder to store the output file
 * @param {string} name the desired name of the output file
 * @param {File} pdf1 the first PDF file
 * @param {File} pdf2 the second PDF file
 * @param {File} opt_pdf3 [optional] the third PDF file; add as many more as you like
 *
 * @return {File} the merged file
 */
function mergePdfs(directory, name, pdf1, pdf2, opt_pdf3) {

  if (name.slice(-4) != '.pdf') {

    name = name + '.pdf';

  }
  var newObjects = ['1 0 obj\r\n<</Type/Catalog/Pages 2 0 R >>\r\nendobj'];
  var pageAddresses = [];
  for (var argumentIndex = 2; argumentIndex < arguments.length; argumentIndex++) {

    var bytes = arguments[argumentIndex].getBlob().getBytes();
    var xrefByteOffset = '';
    var byteIndex = bytes.length - 1;
    while (!/\sstartxref\s/.test(xrefByteOffset)) {

      xrefByteOffset = String.fromCharCode(bytes[byteIndex]) + xrefByteOffset;
      byteIndex--;

    }
    xrefByteOffset = +(/\s\d+\s/.exec(xrefByteOffset)[0]);
    var objectByteOffsets = [];
    var trailerDictionary = '';
    var rootAddress = '';
    do {

      var xrefTable = '';
      var trailerEndByteOffset = byteIndex;
      byteIndex = xrefByteOffset;
      for (byteIndex; byteIndex <= trailerEndByteOffset; byteIndex++) {

        xrefTable = xrefTable + String.fromCharCode(bytes[byteIndex]);

      }
      xrefTable = xrefTable.split(/\s*trailer\s*/);
      trailerDictionary = xrefTable[1];
      if (objectByteOffsets.length < 1) {

        rootAddress = /\d+\s+\d+\s+R/.exec(/\/Root\s*\d+\s+\d+\s+R/.exec(trailerDictionary)[0])[0].replace('R', 'obj');

      }
      xrefTable = xrefTable[0].split('\n');
      xrefTable.shift();
      while (xrefTable.length > 0) {

        var xrefSectionHeader = xrefTable.shift().split(/\s+/);
        var objectNumber = +xrefSectionHeader[0];
        var numberObjects = +xrefSectionHeader[1];
        for (var entryIndex = 0; entryIndex < numberObjects; entryIndex++) {

          var entry = xrefTable.shift().split(/\s+/);
          objectByteOffsets.push([[objectNumber, +entry[1], 'obj'], +entry[0]]);
          objectNumber++;

        }

      }
      if (/\s*\/Prev/.test(trailerDictionary)) {

        xrefByteOffset = +(/\s*\d+\s/.exec(/\s*\/Prev\s*\d+\s/.exec(trailerDictionary)[0])[0]);

      }

    } while (/\s*\/Prev/.test(trailerDictionary));
    var rootObject = getObject(rootAddress, objectByteOffsets, bytes);
    var pagesAddress = /\d+\s+\d+\s+R/.exec(/\/Pages\s*\d+\s+\d+\s+R/.exec(rootObject)[0])[0].replace('R', 'obj');
    var pagesObject = getObject(pagesAddress, objectByteOffsets, bytes);
    var objects = getDependencies(pagesObject, objectByteOffsets, bytes);
    var newObjectsInsertionIndex = newObjects.length;
    for (var objectIndex = 0; objectIndex < objects.length; objectIndex++) {

      var newObjectAddress = [(newObjects.length + 3) + '', 0 + '', 'obj'];
      if (!Array.isArray(objects[objectIndex])) {

        objects[objectIndex] = [objects[objectIndex]];

      }
      objects[objectIndex].unshift(newObjectAddress);
      var objectAddress = objects[objectIndex][1].match(/\d+\s+\d+\s+obj/)[0].split(/\s+/);
      objects[objectIndex].splice(1, 0, objectAddress);
      if (/\/Type\s*\/Page[^s]/.test(objects[objectIndex][2])) {

        objects[objectIndex][2] = objects[objectIndex][2].replace(/\/Parent\s*\d+\s+\d+\s+R/.exec(objects[objectIndex][2])[0], '/Parent 2 0 R');
        pageAddresses.push(newObjectAddress.join(' ').replace('obj', 'R'));

      }
      var addressRegExp = new RegExp(objectAddress[0] + '\\s+' + objectAddress[1] + '\\s+' + 'obj');
      objects[objectIndex][2] = objects[objectIndex][2].replace(addressRegExp.exec(objects[objectIndex][2])[0], newObjectAddress.join(' '));
      newObjects.push(objects[objectIndex]);

    }
    for (var referencingObjectIndex = newObjectsInsertionIndex; referencingObjectIndex < newObjects.length; referencingObjectIndex++) {

      var references = newObjects[referencingObjectIndex][2].match(/\d+\s+\d+\s+R/g);
      if (references != null) {

        var string = newObjects[referencingObjectIndex][2];
        var referenceIndices = [];
        var currentIndex = 0;
        for (var referenceIndex = 0; referenceIndex < references.length; referenceIndex++) {

          referenceIndices.push([]);
          referenceIndices[referenceIndex].push(string.slice(currentIndex).indexOf(references[referenceIndex]) + currentIndex);
          referenceIndices[referenceIndex].push(references[referenceIndex].length);
          currentIndex += string.slice(currentIndex).indexOf(references[referenceIndex]);

        }
        for (var referenceIndex = 0; referenceIndex < references.length; referenceIndex++) {

          var objectAddress = references[referenceIndex].replace('R', 'obj').split(/\s+/);
          for (var objectIndex = newObjectsInsertionIndex; objectIndex < newObjects.length; objectIndex++) {

            if (arrayEquals(objectAddress, newObjects[objectIndex][1])) {

              var length = string.length;
              newObjects[referencingObjectIndex][2] = string.slice(0, referenceIndices[referenceIndex][0]) + newObjects[objectIndex][0].join(' ').replace('obj', 'R') +
                string.slice(referenceIndices[referenceIndex][0] + referenceIndices[referenceIndex][1]);
              string = newObjects[referencingObjectIndex][2];
              var newLength = string.length;
              if (!(length == newLength)) {

                for (var subsequentReferenceIndex = referenceIndex + 1; subsequentReferenceIndex < references.length; subsequentReferenceIndex++) {

                  referenceIndices[subsequentReferenceIndex][0] += (newLength - length);

                }

              }
              break;

            }

          }

        }

      }

    }
    for (var objectIndex = newObjectsInsertionIndex; objectIndex < newObjects.length; objectIndex++) {

      if (Array.isArray(newObjects[objectIndex])) {

        if (newObjects[objectIndex][3] != undefined) {

          newObjects[objectIndex] = newObjects[objectIndex].slice(2);

        } else {

          newObjects[objectIndex] = newObjects[objectIndex][2];

        }

      }

    }

  }
  newObjects.splice(1, 0, '2 0 obj\r\n<</Type/Pages/Count ' + pageAddresses.length + ' /Kids [' + pageAddresses.join(' ') + ' ]>>\r\nendobj');
  newObjects.splice(2, 0, '3 0 obj\r\n<</Title (' + name + ') /CreationDate (D' +
       Utilities.formatDate(new Date(), CalendarApp.getDefaultCalendar().getTimeZone(), 'yyyyMMddHHmmssZ').slice(0, -2) + "'00) /ModDate (D" + Utilities.formatDate(new Date(),
       CalendarApp.getDefaultCalendar().getTimeZone(), 'yyyyMMddHHmmssZ').slice(0, -2) + "'00)>>\r\nendobj");
  var byteOffsets = [0];
  var bytes = [];
  var header = '%PDF-1.3\r\n';
  for (var headerIndex = 0; headerIndex < header.length; headerIndex++) {

    bytes.push(header.charCodeAt(headerIndex));

  }
  bytes.push('%'.charCodeAt(0));
  for (var characterCode = -127; characterCode < -123; characterCode++) {

    bytes.push(characterCode);

  }
  bytes.push('\r'.charCodeAt(0));
  bytes.push('\n'.charCodeAt(0));
  while (newObjects.length > 0) {

    byteOffsets.push(bytes.length);
    var object = newObjects.shift();
    if (Array.isArray(object)) {

      var streamKeyword = /stream\s*\n/.exec(object[0])[0];
      if (streamKeyword.indexOf('\n\n') > streamKeyword.length - 3) {

        streamKeyword = streamKeyword.slice(0, -1);

      } else if (streamKeyword.indexOf('\r\n\r\n') > streamKeyword.length - 5) {

        streamKeyword = streamKeyword.slice(0, -2);

      }
      var streamIndex = object[0].indexOf(streamKeyword) + streamKeyword.length;
      for (var objectIndex = 0; objectIndex < streamIndex; objectIndex++) {

        bytes.push(object[0].charCodeAt(objectIndex))

      }
      bytes = bytes.concat(object[1]);
      for (var objectIndex = streamIndex; objectIndex < object[0].length; objectIndex++) {

        bytes.push(object[0].charCodeAt(objectIndex));

      }

    } else {

      for (var objectIndex = 0; objectIndex < object.length; objectIndex++) {

        bytes.push(object.charCodeAt(objectIndex));

      }

    }
    bytes.push('\r'.charCodeAt(0));
    bytes.push('\n'.charCodeAt(0));

  }
  var xrefByteOffset = bytes.length;
  var xrefHeader = 'xref\r\n';
  for (var xrefHeaderIndex = 0; xrefHeaderIndex < xrefHeader.length; xrefHeaderIndex++) {

    bytes.push(xrefHeader.charCodeAt(xrefHeaderIndex));

  }
  var xrefSectionHeader = '0 ' + byteOffsets.length + '\r\n';
  for (var xrefSectionHeaderIndex = 0; xrefSectionHeaderIndex < xrefSectionHeader.length; xrefSectionHeaderIndex++) {

    bytes.push(xrefSectionHeader.charCodeAt(xrefSectionHeaderIndex));

  }
  for (var byteOffsetIndex = 0; byteOffsetIndex < byteOffsets.length; byteOffsetIndex++) {

    for (var byteOffsetStringIndex = 0; byteOffsetStringIndex < 10; byteOffsetStringIndex++) {

      bytes.push(Utilities.formatString('%010d', byteOffsets[byteOffsetIndex]).charCodeAt(byteOffsetStringIndex));

    }
    bytes.push(' '.charCodeAt(0));
    if (byteOffsetIndex == 0) {

      for (var generationStringIndex = 0; generationStringIndex < 5; generationStringIndex++) {

        bytes.push('65535'.charCodeAt(generationStringIndex));

      }
      for (var keywordIndex = 0; keywordIndex < 2; keywordIndex++) {

        bytes.push(' f'.charCodeAt(keywordIndex));

      }

    } else {

      for (var generationStringIndex = 0; generationStringIndex < 5; generationStringIndex++) {

        bytes.push('0'.charCodeAt(0));

      }
      for (var keywordIndex = 0; keywordIndex < 2; keywordIndex++) {

        bytes.push(' n'.charCodeAt(keywordIndex));

      }

    }
    bytes.push('\r'.charCodeAt(0));
    bytes.push('\n'.charCodeAt(0));

  }
  for (var trailerHeaderIndex = 0; trailerHeaderIndex < 9; trailerHeaderIndex++) {

    bytes.push('trailer\r\n'.charCodeAt(trailerHeaderIndex));

  }
  var idBytes = Utilities.computeDigest(Utilities.DigestAlgorithm.MD5, (new Date).toString());
  var id = '';
  for (var idByteIndex = 0; idByteIndex < idBytes.length; idByteIndex++) {

    id = id + ('0' + (idBytes[idByteIndex] & 0xFF).toString(16)).slice(-2);

  }
  var trailer = '<</Size ' + (byteOffsets.length) + ' /Root 1 0 R /Info 2 0 R /ID [<' + id + '> <' + id + '>]>>\r\nstartxref\r\n' + xrefByteOffset + '\r\n%%EOF';
  for (var trailerIndex = 0; trailerIndex < trailer.length; trailerIndex++) {

    bytes.push(trailer.charCodeAt(trailerIndex));

  }
  return directory.createFile(Utilities.newBlob(bytes, 'application/pdf', name));
  function getObject(objectAddress, objectByteOffsets, bytes) {

    objectAddress = objectAddress.split(/\s+/);
    for (var addressIndex = 0; addressIndex < 2; addressIndex++) {

      objectAddress[addressIndex] = +objectAddress[addressIndex];

    }
    var object = [];
    var byteIndex = 0;
    for each (var offset in objectByteOffsets) {

      if (arrayEquals(objectAddress, offset[0])) {

        byteIndex = offset[1];
        break;

      }

    }
    object.push('');
    while (object[0].indexOf('endobj') <= -1) {

      if (/stream\s*\n/.test(object[0])) {

        var streamLength;
        var lengthFinder = object[0].slice(object[0].indexOf(/\/Length/.exec(object[0])[0]));
        if (/\/Length\s*\d+\s+\d+\s+R/.test(lengthFinder)) {

          var lengthObjectAddress = /\d+\s+\d+\s+R/.exec(/\/Length\s*\d+\s+\d+\s+R/.exec(lengthFinder)[0])[0].split(/\s+/);
          lengthObjectAddress[2] = 'obj';
          for (var addressIndex = 0; addressIndex < 2; addressIndex++) {

            lengthObjectAddress[addressIndex] = +lengthObjectAddress[addressIndex];

          }
          var lengthObject = ''
          var lengthByteIndex = 0;
          for each (var offset in objectByteOffsets) {

            if (arrayEquals(lengthObjectAddress, offset[0])) {

              lengthByteIndex = offset[1];
              break;

            }

          }
          while (lengthObject.indexOf('endobj') <= -1) {

            lengthObject = lengthObject + String.fromCharCode(bytes[lengthByteIndex]);
            lengthByteIndex++;

          }
          streamLength = +(lengthObject.match(/obj\s*\n\s*\d+\s*\n\s*endobj/)[0].match(/\d+/)[0]);

        } else {

          streamLength = +(/\d+/.exec(lengthFinder)[0]);

        }
        var streamBytes = bytes.slice(byteIndex, byteIndex + streamLength);
        object.push(streamBytes);
        byteIndex += streamLength;
        while (object[0].indexOf('endobj') <= -1) {

          object[0] = object[0] + String.fromCharCode(bytes[byteIndex]);
          byteIndex++;

        }
        return object;

      }
      object[0] = object[0] + String.fromCharCode(bytes[byteIndex]);
      byteIndex++;

    }
    return object[0];

  }
  function arrayEquals(array1, array2) {

    if (array1 == array2) {

      return true;

    }
    if (array1 == null && array2 == null) {

      return true;

    } else if (array1 == null || array2 == null) {

      return false;

    }
    if (array1.length != array2.length) {

      return false;

    }
    for (var index = 0; index < array1.length; index++) {

      if (Array.isArray(array1[index])) {

        if (!arrayEquals(array1[index], array2[index])) {

          return false;

        }
        continue;

      }
      if (array1[index] != array2[index]) {

        return false;

      }

    }
    return true;

  }
  function getDependencies(objectString, objectByteOffsets, bytes) {

    var dependencies = [];
    var references = objectString.match(/\d+\s+\d+\s+R/g);
    if (references != null) {

      while (references.length > 0) {

        if (/\/Parent/.test(objectString.slice(objectString.indexOf(references[0]) - 8, objectString.indexOf(references[0])))) {

          references.shift();
          continue;

        }
        var dependency = getObject(references.shift().replace('R', 'obj'), objectByteOffsets, bytes);
        var dependencyExists = false;
        for each (var entry in dependencies) {

          dependencyExists = (arrayEquals(dependency, entry)) ? true : dependencyExists;

        }
        if (!dependencyExists) {

          dependencies.push(dependency);

        }
        if (Array.isArray(dependency)) {

          dependencies = dependencies.concat(getDependencies(dependency[0], objectByteOffsets, bytes));

        } else {

          dependencies = dependencies.concat(getDependencies(dependency, objectByteOffsets, bytes));

        }

      }

    }
    return dependencies;

  }

}

基本上发生的事情是,每个文件都有包含其页面、内容和资源的对象进行识别。然后这些对象将被重新编号并用新的“包装”格式化为新文件。

我编写这段代码以供两个文件使用,但我想象可能需要更多,所以我让代码适用于那种情况。为了对原始问题有效,函数的开头

function(mergePdfs(directory, name, pdf1, pdf2, opt_pdf3) {

  if (name.slice(-4) != '.pdf') {

    name = name + '.pdf';

  }
  var newObjects = ['1 0 obj\r\n<</Type/Catalog/Pages 2 0 R >>r\nendobj'];
  var pageAddresses = [];
  for (var argumentIndex = 2; argumentIndex < arguments.length; argumentIndex++) {

    var bytes = arguments[argumentIndex].getBlob().getBytes();

应该被替换为

function mergePdfs(directory, name) {

  if (name.slice(-4) != '.pdf') {

    name = name + '.pdf';

  }
  var newObjects = ['1 0 obj\r\n<</Type/Catalog/Pages 2 0 R >>\r\nendobj'];
  var pageAddresses = [];
  var files = directory.getFiles();
  for (var fileIndex = 0; fileIndex < files.length; fileIndex++) {

    var bytes = files[fileIndex].getBlob().getBytes();

1
这段代码对我进行的所有测试都是可用的。我还有一种拆分PDF的方法。您可以在此处查看此代码以及我对其进行的任何更新。我已经公开了这个,因为我认为没有API是一件悲惨的事情。 - pokyCoder
1
非常感谢。非常感谢。非常感谢。这正是我一直在寻找的。在我的测试运行中完美地工作了。对于我来说,这个PDF东西是一个全新的丛林。我将打印出来并阅读,以便为自己而努力跟进。最初注意到的一个奇怪之处?大约在第362行附近有一个返回命令,没有闭合的},然后是一个新函数?你知道关于用)结束js函数的事情吗?没有打印出来很难弄清楚发生了什么,但它通过了检验并且可以工作。你能给出任何指针来帮助我理解吗?再次感谢。 - bryanp
该函数并未在那一点结束。下一行开始的函数是该函数的一部分。嵌套函数限制了导入此代码的脚本可以使用的功能。在这种情况下,没有安全问题;我只是不希望在调用其中一个函数时,在编辑器中出现额外的函数选项。希望这样说得清楚。 - pokyCoder
1
我现在可以确认,“for each”循环与V8不兼容。我已经更新了我的个人代码,所以链接版本现在应该可以工作了。 - pokyCoder
1
1.4 不被接受。 - pokyCoder
显示剩余8条评论

5

我也遇到了同样的问题,目前我正在暂时使用一个 RestFul API 来合并 PDF 文件:https://www.convertapi.com/pdf-to-merge

function merge() {
  var folder = DriveApp.getFolderById('<ID FOLDER>'); // folder with files pdf
  var files = folder.getFiles(); // get all files pdf
  
  var formData = {};
  var index = 0;
  while(files.hasNext()) {
    var file = files.next();
    formData['Files[' + index + ']'] = file.getBlob();
    index++;
  }
  
  var options = {
    'method' : 'post',
    'payload' : formData,
    'muteHttpExceptions': true
  };
 
  var response = UrlFetchApp.fetch('https://v2.convertapi.com/pdf/to/merge?Secret=<YOUR SECRET>', options);
  
  if(response.getResponseCode() == 200) {
    var contentText = JSON.parse(response.getContentText());
    var blob = Utilities.base64Decode(contentText.Files[0].FileData);
    folder.createFile(Utilities.newBlob(blob, 'application/pdf', 'merge.pdf'));
  }
}


感谢您发布这个答案。这是最好的合并解决方案! - Yeti
在将 file.getBlob() 更改为 file.getBlob().getAs("application/pdf") 后,这对我起作用了。 - Yeti

5

希望对于希望使用外部JavaScript库合并PDF文档的人有所帮助。我将合并PDF函数分离,以便于其他参考此问题的人更好地兼容。请注意,由于这是使用外部库,可能需要进行修改以使其在未来正常工作。

eval(UrlFetchApp.fetch("https://unpkg.com/pdf-lib/dist/pdf-lib.js").getContentText());
setTimeout = (func, sleep) => (Utilities.sleep(sleep), func())

/**
 * Returns merged pdf, blobs are merged in the same order they are proivded.
 * @param {Blob[]} blobs Blob array
 * @param {String} fileName output PDF name
 * @return {Promise} Promise object, blob of merged blobs
 */
async function mergeAllPDFs(blobs, fileName) {
  const pdf = await PDFLib.PDFDocument.create();
  for (let i = 0; i < blobs.length; i++) {
    const tempBytes = await new Uint8Array(blobs[i].getBytes());
    const tempPdf = await PDFLib.PDFDocument.load(tempBytes);
    const pages = tempPdf.getPageCount();
    for (let p = 0; p < pages; p++) {
      const [tempPage] = await pdf.copyPages(tempPdf, [p]);
      pdf.addPage(tempPage);
    }
  }
  const pdfDoc = await pdf.save()
  return Utilities.newBlob(pdfDoc).setName(fileName)
}

async function MergeFiles() {
  var folder = DriveApp.getFolderById('myFolderID');
  var files = folder.getFiles();
  var blobs = [];
  while (files.hasNext()) {
    var file = files.next();
    blobs.push(file.getBlob());
  }
  var myPDF = await mergeAllPDFs(blobs, "newPDF.pdf")
  folder.createFile(myPDF);
}

有用的资源:

正在使用的外部库 pdf-lib.js

如何使用外部 JavaScript 库


这太棒了!谢谢你。我建议将pdf-lib的fetch调用移动到mergeAllPDFs()函数内,因为如果您想将此应用程序脚本部署为库以在其他脚本中使用,则会在运行不依赖于此包的函数时获取pdf-lib,而如果您将其移动到函数顶部的mergeAllPDFs内,那么只有在需要时才会下载pdf-lib。 - mschwartz
我还建议使用压缩版本,其大小为原版的三分之一:https://unpkg.com/pdf-lib/dist/pdf-lib.min.js - mschwartz
还有一个优化,我的一些合并后的PDF文件大小是输入PDF文件的2-3倍。我将问题缩小到在for循环内多次调用pdf.copyPages。为了解决这个问题,我使用了以下代码:const pageCount = tempPdf.getPageCount()
const pageIndicesArray = [...Array(pageCount).keys()]
const pages = await pdf.copyPages(tempPdf, pageIndicesArray)
pages.forEach(page => pdf.addPage(page))
- mschwartz

3

一个多页PDF绝对不是多个PDF文件内容的简单拼接...即使我承认这种方法似乎很诱人,我怀疑你也无法通过这种方法获得任何结果。

我也一直在寻找这样的东西,但迄今为止没有成功。


1
你有想出什么办法吗?我无法弄清如何连接 blob 数组。 - user1518316

0

.getBytes() 方法为每个文件返回一个二进制数组,因此您创建的是一个包含以下内容的数组:

blobs.push(files[i].getBlob().getBytes());

相反,我会将循环中当前项的数组与一个累加器数组连接起来,该数组随着每次循环迭代而增长。然后,在退出循环后,可以将累加器数组的内容传递到.newBlob().setBytes()中。


我想不出一种将 blob 数组连接起来的方法。还有其他的想法/代码片段吗? - user1518316

0

你的代码正在按照你编写的方式运行。 blobs是一个数组,当你执行blobs.pop()时,你会得到数组中的最后一项(也就是你的最后一个PDF)。

你应该做的是将这些blob连接起来,不是在一个数组中,而是在单个的blob对象中。但是,我不确定在GAS中如何实现这一点。


-1
这是连接 blob 的代码,但正如 Serge 提到的那样,它实际上并不起作用,PDF 文件会出现损坏。
function MergeFiles(){
var folder = DocsList.getFolderById('myFolderID'); 
var files = folder.getFiles(); 

var blobs = "";    
for( var i in files )   
blobs=blobs+(files[i].getBlob().getBytes());
Logger.log(blobs);
var myPDF = Utilities.newBlob(blobs, "application/pdf", "newPDF.pdf");
folder.createFile(myPDF);

    }

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