Home > Blockchain >  Tanaike's Merge PDF script as web app does not return the file ID?
Tanaike's Merge PDF script as web app does not return the file ID?

Time:01-15

I can successfully run Tanaike's Merge PDF script in a google apps script web app. I want to return the file ID of the resultant pdf, but this does not appear possible due to the async function to merge the pdfs. How do I modify my web app code to return the file ID ?

Tanaike suggests a solution in the comments here, which I have attempted to follow, but it does not work for me.

function doGet(e) {
var pdfIds = JSON.parse(e.parameter.pdfIds);
var filename = e.parameter.filename;
return mergePdf(pdfIds,filename);   //<<<<<<<
}

async function mergePdf(pdfIds,filename) {

  const ids = pdfIds;
  const data = ids.map((id) => new Uint8Array(DriveApp.getFileById(id).getBlob().getBytes()));

  // Merge PDFs.
  const cdnjs = "https://cdn.jsdelivr.net/npm/pdf-lib/dist/pdf-lib.min.js";
  eval(UrlFetchApp.fetch(cdnjs).getContentText()); // Load pdf-lib
  const setTimeout = function(f, t) {
    Utilities.sleep(t);
    return f();
  }
  const pdfDoc = await PDFLib.PDFDocument.create();
  for (let i = 0; i < data.length; i  ) {
    const pdfData = await PDFLib.PDFDocument.load(data[i]);
    const pages = await pdfDoc.copyPages(pdfData, [...Array(pdfData.getPageCount())].map((_, i) => i));
    pages.forEach(page => pdfDoc.addPage(page));
  }
  const bytes = await pdfDoc.save();

  // Create the merged PDF file.
  const mergedPdf = DriveApp.createFile(Utilities.newBlob([...new Int8Array(bytes)], MimeType.PDF, filename));
  const fileId = mergedPdf.getId();  //<<<<<<
  return ContentService.createTextOutput(fileId); //<<<<<<
}

CodePudding user response:

Please think of this answer as one of several possible answers.

Modification points:

  • I think that in your situation, the method for giving multiple file IDs is required to be known. But, unfortunately, from your question, I cannot know about it. So, in this answer, about the method for requesting Web Apps, I would like to use the following sample curl command.

    $ curl -L "https://script.google.com/macros/s/###/exec?pdfIds=###fileId1###&pdfIds=###fileId2###&filename=sampleFilename.pdf"
    
  • When I saw your script, doGet(e) is required to be modified. The event object has already been pased as a JSON object. And, in that request, var pdfIds = JSON.parse(e.parameter.pdfIds); is required to be modified to var pdfIds = e.parameters.pdfIds;.

  • And, it seems that in the current stage, when the values returned from the async function are used with ContentService.createTextOutput, the values are not supported type. So, when you want to return the file ID, it is required to consider this.

When these points are reflected in your script, how about the following modification?

Modified script:

function doGet(e) {
  var pdfIds = e.parameters.pdfIds;
  var filename = e.parameter.filename;
  mergePdf(pdfIds, filename);
  Utilities.sleep(5000); // <--- Please adjust this value.
  var c = CacheService.getScriptCache();
  var fileId = c.get("fileId");
  if (fileId) {
    c.remove("fileId");
    return ContentService.createTextOutput(fileId);
  }
  return ContentService.createTextOutput("File ID is not found. Please adjust Utilities.sleep(5000).");
}

async function mergePdf(pdfIds, filename) {
  const ids = pdfIds;
  const data = ids.map((id) => new Uint8Array(DriveApp.getFileById(id).getBlob().getBytes()));

  // Merge PDFs.
  const cdnjs = "https://cdn.jsdelivr.net/npm/pdf-lib/dist/pdf-lib.min.js";
  eval(UrlFetchApp.fetch(cdnjs).getContentText()); // Load pdf-lib
  const setTimeout = function (f, t) {
    Utilities.sleep(t);
    return f();
  }
  const pdfDoc = await PDFLib.PDFDocument.create();
  for (let i = 0; i < data.length; i  ) {
    const pdfData = await PDFLib.PDFDocument.load(data[i]);
    const pages = await pdfDoc.copyPages(pdfData, [...Array(pdfData.getPageCount())].map((_, i) => i));
    pages.forEach(page => pdfDoc.addPage(page));
  }
  const bytes = await pdfDoc.save();

  // Create the merged PDF file.
  const mergedPdf = DriveApp.createFile(Utilities.newBlob([...new Int8Array(bytes)], MimeType.PDF, filename));
  const fileId = mergedPdf.getId();
  CacheService.getScriptCache().put("fileId", fileId);
}
  • When you modified the Google Apps Script of Web Apps, please modify the deployment as a new version. By this, the modified script is reflected in Web Apps. Please be careful about this.

  • In this case, when you run a curl command like curl -L "https://script.google.com/macros/s/###/exec?pdfIds=###fileId1###&pdfIds=###fileId2###&filename=sampleFilename.pdf", the multiple PDF files are merged and a file ID of the created new PDF file is returned.

Note:

  • In this sample, I used CacheService. But, in this case, a global variable and PropetiesService might be able to be also used.

  • When you modified the Google Apps Script of Web Apps, please modify the deployment as a new version. By this, the modified script is reflected in Web Apps. Please be careful about this.

  • You can see the detail of this in my report "Redeploying Web Apps without Changing URL of Web Apps for new IDE (Author: me)".

  • Related