1

I'm new to JavaScript, and am trying to write some code that uses the google drive API (via the gapi client) to transform an existing slide into a pdf document, upload it to a specific folder, and return the pdf file id. This is all to be done in the browser, if possible.

I've already done this on python for another use case, and the code looks something like this:

import googleapiclient.http as client_methods
from io import BytesIO
...
data = drive.files().export(fileId=slideId, mimeType='application/pdf').execute()
body = {'name': fileName, 'mimeType': 'application/pdf', 'parents': [folderId]}
# wrapping the binary (data) file with BytesIO class
fh = io.BytesIO(data)
# creating the Media Io upload class for the file
media_body = client_methods.MediaIoBaseUpload(fh, mimetype='application/pdf')
pdfFileId = drive.files().create(body=body, media_body=media_body, supportsAllDrives=True).execute(['id'])

I've tried to replicate the same steps using JavaScript and my limited knowledge, and can successfully upload a pdf file into the desired folder, but the file shows as empty (doesn't even open in the drive).

I believe it might be due to the way I'm handling the binary data that I get from exporting the initial slide.

The last iteration of my JavaScript code is shown below (I have all the necessary permissions to use the gapi client):

async function createPdfFile() {
 gapi.client.load("drive", "v3", function () {
   // Set the MIME type for the exported file
   const mimeType = "application/pdf";

   // Set the file name for the exported PDF file
   const fileName = "Trial upload.pdf";

   // Export the Google Slides presentation as a PDF file
   gapi.client.drive.files.export({
     fileId,
     mimeType
   }).then(async function (response) {
     // Get the binary data of the PDF file
     const pdfData = await response.body;
    
     const blob = await new Blob([pdfData], {type: 'application/pdf'})
     const file = new File([blob], "presentation.pdf");

     // Create a new file in the specified Google Drive folder with the PDF data
     await gapi.client.drive.files.create({
       name: fileName,
       parents: [folderId],
       mimeType: mimeType,
       media: {mimeType: 'application/pdf', body: file},
       supportsAllDrives: true
     }).then(function (response) {
       // Get the ID of the created PDF file
       const pdfFileId = response.result.id;
       console.log("PDF file created with ID: " + pdfFileId);
     })
   })
 })
}
await createPdfFile() 

As for the output, and as stated, it does create a pdf file, and logs the pdf file id, but the file itself is empty. I'd really appreciate it if someone could help me make sense of this (similar thread here, but can't replicate his success).

1 Answer 1

0

I believe your goal is as follows.

  • You want to convert Google Slides to PDF format using googleapis for Javascript.
  • Your access token can be exported and uploaded to Google Drive.

Issue and workaround:

When I tested your script, unfortunately, response.body from gapi.client.drive.files.export is binary data, and in this case, this cannot be correctly converted to the blob. And also, in the current stage, it seems that a file cannot be uploaded using gapi.client.drive.files.create. I thought that these might be the reason for your current issue.

From these situations, I would like to propose the flow for achieving your goal using fetch API. The modified script is as follows.

In this case, the access token is retrieved from the client like gapi.auth.getToken().access_token.

Modified script:

Please modify your script as follows.

From:

gapi.client.drive.files.export({
  fileId,
  mimeType
}).then(async function (response) {
  // Get the binary data of the PDF file
  const pdfData = await response.body;

  const blob = await new Blob([pdfData], { type: 'application/pdf' })
  const file = new File([blob], "presentation.pdf");

  // Create a new file in the specified Google Drive folder with the PDF data
  await gapi.client.drive.files.create({
    name: fileName,
    parents: [folderId],
    mimeType: mimeType,
    media: { mimeType: 'application/pdf', body: file },
    supportsAllDrives: true
  }).then(function (response) {
    // Get the ID of the created PDF file
    const pdfFileId = response.result.id;
    console.log("PDF file created with ID: " + pdfFileId);
  })
})

To:

gapi.client.drive.files.get({ fileId, fields: "exportLinks", supportsAllDrives: true }).then(function (response) {
  const obj = JSON.parse(response.body);
  if (Object.keys(obj).length == 0) throw new Error("This file cannot be converted to PDF format.");
  const url = obj.exportLinks["application/pdf"];
  if (!url) throw new Error("No exported URL.");
  const accessToken = gapi.auth.getToken().access_token;
  fetch(url, {
    method: 'GET',
    headers: { 'Authorization': 'Bearer ' + accessToken },
  })
    .then(res => res.blob())
    .then(blob => {
      const metadata = { name: fileName, parents: [folderId], mimeType };
      const form = new FormData();
      form.append('metadata', new Blob([JSON.stringify(metadata)], { type: 'application/json' }));
      form.append('file', blob);
      fetch('https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart&supportsAllDrives=true', {
        method: 'POST',
        headers: { 'Authorization': 'Bearer ' + accessToken },
        body: form
      })
        .then(res => res.json())
        .then(obj => console.log("PDF file created with ID: " + obj.id));
    });
});
  • When this script is run, the export URL of PDF data is retrieved from the file ID. And, the PDF data is downloaded and uploaded to Google Drive.

Note:

  • In your script, fileId is not declared. Please be careful about this.

  • If the file size is more than 5 MB, please use the resumable upload.

Reference:

Added:

From your following reply,

?uploadType=multipart also returns a 404 type error

I'm worried about that in your situation, new FormData() might not be able to be used. If my understanding is correct, please test the following script. In this script, the request body of multipart/form-data is manually created.

Modified script:

gapi.client.drive.files.get({ fileId, fields: "exportLinks", supportsAllDrives: true }).then(function (response) {
  const obj = JSON.parse(response.body);
  if (Object.keys(obj).length == 0) throw new Error("This file cannot be converted to PDF format.");
  const url = obj.exportLinks["application/pdf"];
  if (!url) throw new Error("No exported URL.");
  const accessToken = gapi.auth.getToken().access_token;
  fetch(url, {
    method: 'GET',
    headers: { 'Authorization': 'Bearer ' + accessToken },
  })
    .then(res => res.blob())
    .then(blob => {
      const metadata = { name: fileName, parents: [folderId], mimeType };
      const fr = new FileReader();
      fr.onload = e => {
        const data = e.target.result.split(",");
        const req = "--xxxxxxxx\r\n" +
          "Content-Type: application/json\r\n\r\n" +
          JSON.stringify(metadata) + "\r\n" +
          "--xxxxxxxx\r\n" +
          "Content-Transfer-Encoding: base64\r\n\r\n" +
          data[1] + "\r\n" +
          "--xxxxxxxx--";
        fetch('https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart&supportsAllDrives=true', {
          method: 'POST',
          headers: { 'Authorization': 'Bearer ' + accessToken, "Content-Type": "multipart/related; boundary=xxxxxxxx" },
          body: req
        })
          .then(res => res.json())
          .then(obj => {
            console.log("PDF file created with ID: " + obj.id)
          });
      }
      fr.readAsDataURL(blob);
    });
});
  • When I tested this script, no error occurs. I confirmed that the Google Slides file could be converted to a PDF file and the PDF file was uploaded to the specific folder.
Sign up to request clarification or add additional context in comments.

18 Comments

Hello, thank you so much for your help! This works, to an extent. Full disclosure, I tried to use the fetch API before, but noticed 2 things: 1. the files weren't getting uploaded to the desired folder, but instead were uploaded to my personal drive 2. they were uploaded as "Untitled" (no name and .pdf). These 2 still happen with your code. However, in my approach, downloading the Untitled files and changing them to Untitled.pdf still showed an empty slide, whilst yours has the desired slide data! So there's progress, at least
Also, small correction, you have to fetch googleapis.com/upload/drive/v3/files?uploadType=media, else it won't work at all
@John Neves Thank you for replying. About 1. the files weren't getting uploaded to the desired folder, when I tested my script, the file is uploaded to the specific folder. About 2. they were uploaded as "Untitled" (no name and .pdf)., when I tested my script, the correct filename is given and the file is uploaded. From this situation, I'm worried that you might have miscopied. So, can you provide your current script? By this, I would like to confirm it. I think that the reason for the no error when I tested my script is due to my poor skill. I apologize for this.
@John Neves When I tested my script again, no error occurs, and the Google Slides file is converted to a PDF file, and then, the converted PDF file is uploaded to the specific folder. I deeply apologize that when I test it, my script works fine. Unfortunately, I cannot still replicate your situation. I think that this is due to my poor skill. I apologize for my poor skill again. I think that in this case, it is required to know your current script. If you can cooperate to resolve your issue, I'm glad. Can you cooperate to do it?
no problem, no need to apologize, you're being very helpful. MY poor skill meant I had to post another answer with the code, can you please take a look and see what I'm missing?
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.