Skip to content
Advertisement

Upload a file from URL to Google Drive with Google Apps Script

I am trying to upload a file to my Google Drive instance through Google Apps Script.

I have the URL of the file, but due to my local server’s nature (running locally) I can only download it locally. This means that I can only manage the file locally, and I only the response can be sent to my Google Apps Script.

The response (from list of available files) I get looks like this:

{
    "contextid": 40,
    "component": "mod_folder",
    "filearea": "content",
    "itemid": 0,
    "filepath": "/",
    "filename": "mypdf.pdf",
    "isdir": false,
    "url": "http://localhost/server/pluginfile.php/40/mod_folder/content/0/mypdf.pdf",
    "timemodified": 1645463016,
    "timecreated": 1645462995,
    "filesize": 1504856,
    "author": "Admin",
    "license": "unknown"
}

and doing axios.get() with the url from above (and some authentication), returns this JSON object:

{
    "config": {transitional: {...}, transformRequest: Array(1),...},
    "data": "%PDF-1.5rn%����rn1 0 objrn<</Type/Catalog/Pages 2 0 R/Lang(en-GB) /StructTreeRoot 70 0 R/MarkInfo<</Marked true>>>>rnendobjrn2 0.......",
    "headers": "{accept-ranges: 'bytes', cache-control: 'private, must-revalidate, pre-check=0, post-check=0, max-age=0, no-transform',..."
    "request": "XMLHttpRequest {onreadystatechange: null, readyState: 4, timeout: 0, withCredentials: false, upload: XMLHttpRequestUpload, …}",
    "status": 200
    "statusText": "OK"
}

The value of data seems to represent the stringified value of mypdf.pdf, and my plan was to send it as a string to my Script, create a blob out of it, and upload to my drive.


My JS client script looks like this:

     axios.post('https://script.google.com/macros/s/AKfy....XEby/exec', { type: 'application/pdf', file: r.data })
/*A*/     .then(r => console.log(r))
          .catch(e => console.error(e));

and my Google Apps Script:

function doPost(e) {
  const folderId = "root";
  const blob = Utilities.newBlob(JSON.parse(e.postData.contents.file), 'application/pdf', 'mypdf.pdf');
  const file = DriveApp.getFolderById(folderId).createFile(blob);
  const responseObj = {filename: file.getName(), fileId: file.getId(), fileUrl: file.getUrl()};
  return ContentService.createTextOutput(JSON.stringify(responseObj));
}

Unfortunately, /A/ in my script keeps returning the same message, i.e.

{
    "config": {transitional: {...}, transformRequest: Array(1),...},
    "data": "<!DOCTYPE html><html><head><link rel="shortcut icon" href="//ssl.gstatic.com/docs/script/images/favicon.ico"><title>Error</title><style ty",
    "headers": "{alt-svc: 'h3=":443"; ma=2592000,h3-29=":443"; ma=2592000,h3-…3"; ma=2592000,quic=":443"; ma=2592000; v="46,43"',..."
    "request": "XMLHttpRequest {onreadystatechange: null, readyState: 4, timeout: 0, withCredentials: false, upload: XMLHttpRequestUpload, …}",
    "status": 200
    "statusText": ""
}

which is in fact an error, and the value of data indicates that there is SyntaxError: Unexpected token u in JSON at position 0 (line 5, file "Code"). What is wrong with my line 5? Is it my JSON.parse()?


I am aware that uploading an actual content of my mypdf.pdf file and then wrapping it in a blob in the script might not be the best idea. However, I won’t be able to send the url, because it will not work remotely. I am really confused and not sure how to solve the issue. Why am I getting this message? Do I need to wrap the response (actual pdf contents) in a blob before posting it to my script?

Advertisement

Answer

Unfortunately, I cannot see your whole script from your question. So this proposed modification is just my guess by understanding your question.

Modification points:

  • From your following sample data from doing axios.get() with the url from above (and some authentication), returns this JSON object:,

      {
          "config": {transitional: {...}, transformRequest: Array(1),...},
          "data": "%PDF-1.5rn%����rn1 0 objrn<</Type/Catalog/Pages 2 0 R/Lang(en-GB) /StructTreeRoot 70 0 R/MarkInfo<</Marked true>>>>rnendobjrn2 0.......",
          "headers": "{accept-ranges: 'bytes', cache-control: 'private, must-revalidate, pre-check=0, post-check=0, max-age=0, no-transform',..."
          "request": "XMLHttpRequest {onreadystatechange: null, readyState: 4, timeout: 0, withCredentials: false, upload: XMLHttpRequestUpload, …}",
          "status": 200
          "statusText": "OK"
      }
    
  • I thought that in your script, you might have used axios.get without options. When axios.get is used as the default, the binary data is retrieved as the string. I thought that this might be the reason for your issue.

  • In this case, please use an option of {responseType: 'arraybuffer'}. By this, the binary data can be obtained as the buffer. Using this, the data is sent to your Web Apps as the int8array data. By this, the data can be used with Utilities.newBlob.

  • When I saw your Google Apps Script, only file is used with const blob = Utilities.newBlob(JSON.parse(e.postData.contents.file), 'application/pdf', 'mypdf.pdf').

  • And also JSON.parse(e.postData.contents.file) is required to be JSON.parse(e.postData.contents).file. Please be careful this.

When this issue is removed, the modified script is as follows.

Axios side:

In order to retrieve the data of doing axios.get() with the url from above (and some authentication), returns this JSON object:, please set the URL to contentUrl. And also, please set your Web Apps URL to webAppsUrl.

const contentUrl = "###"; // Please set the URL of conente you want to download.
const webAppsUrl = "https://script.google.com/macros/s/###/exec"; // Please set your Web Apps URL.

axios.get(contentUrl, {responseType: "arraybuffer"})
.then(res => {
  axios.post(webAppsUrl, JSON.stringify([...new Int8Array(res.data)]))
  .then((res) => console.log(res.data))
  .catch((err) => console.log(err));
})
.catch(err => console.log(err));
  • I cannot your whole script. So please modify it using my proposed script.

  • Unfortunately, in the current stage, Web Apps cannot use the binary data. So, the retrieved binary data is sent by converting to string value. But, in this case, in order to decode the data, the binary data is converted to the int8array data.

Google Apps Script side:

function doPost(e) {
  const folderId = "root";
  const blob = Utilities.newBlob(JSON.parse(e.postData.contents), 'application/pdf', 'mypdf.pdf'); // Modified
  const file = DriveApp.getFolderById(folderId).createFile(blob);
  const responseObj = { filename: file.getName(), fileId: file.getId(), fileUrl: file.getUrl() };
  return ContentService.createTextOutput(JSON.stringify(responseObj));
}

Note:

  • Although in your question, the setting of Web Apps is not included, from your script, I guessed that the setting of Web Apps is Execute as: Me and Who has access to the app: Anyone (using new IDE). Please be careful about this.
  • When I tested my proposed script using a sample URL of contentUrl, I could confirm that the PDF data can be sent to Web Apps and create it as a file. But, when you test it and an error occurs, please confirm your setting of Web Apps and script, again.
  • When you modified the Google Apps Script, please modify the deployment as a new version. By this, the modified script is reflected in Web Apps. Please be careful this.
  • You can see the detail of this in the report of “Redeploying Web Apps without Changing URL of Web Apps for new IDE“.

References:

User contributions licensed under: CC BY-SA
2 People found this is helpful
Advertisement