This post is following my previous one ->
How to turn Postman API's request into Apps Script code?
I'm trying to convert 3 API requests into a single piece of code via Google Apps Script.
The purpose is to export automatically a set of data from my CRM platform into Google Sheet.
Based on my CRM platform documentation, I need to do 3 API requests in order to get the final set of data I want:
Creating an export job (using a POST request)
Retrieve the job status from previous request (using GET request)
Retrieve the exported data (using GET request)
Below is the code for the 1st API request (kindly provided by Tanaike):
function exportjob() {
var url = 'https://api.intercom.io/export/content/data';
var options = {
"method": "post",
"headers": {
"Authorization": "Bearer 123456789",
"Accept": "application/json",
},
"contentType": "application/json",
"payload": JSON.stringify({
"created_at_after": 1654041600,
"created_at_before": 1656547200
})
}
var response = UrlFetchApp.fetch(url, options);
console.log(response.getContentText())
}
The result of this request is the following
Info {"job_identifier":"5gf58ty4y5y45229r", "status":"pending", "download_url":"", "download_expires_at":""}
Next step is to add the 2 other API requests in my script (Retrieve the job status and retrieve the data).
However I have a couple of issues:
For the 2nd API request
How do I include the job_identifier ID in my code? (given in the 1st request)
For the 3rd API request
How do I retrieve the data with the URL provided in the 2nd API request?
The data comes as ZIP file, how do I convert it to CSV so Google Sheet can open it?
How do get the final set of data to be exported in a google sheet?
Apologies for the long post, I'm trying to summarize the documentation from my CRM platform as much as I can.
Any help on how to retrieve the job POST and retrieve the set of data to a google sheet via CSV would be highly appreciated.
Thank you
1. About For the 2nd API request
If this is for Retrieve a job status, it seems that the sample curl command is as follows.
curl https://api.intercom.io/export/content/data/v5xsp8c19ji0s82 \
-X GET \
-H 'Authorization:Bearer <Your access token>'
-H 'Accept: application/json'
It seems that the value of job_identifier can be retrieved from your 1st request shown in your question. Ref
When this request is done for the first time, it seems that status returns pending. By this, it seems that until status is changed to complete, it is required to wait. Ref
2. About For the 3rd API request
If this is for Retrieve the exported data, it seems that the sample curl command is as follows.
curl https://api.intercom.io/download/content/data/xyz1234 \
-X GET \
-H 'Authorization:Bearer <Your access token>' \
-H 'Accept: application/octet-stream’
In this case, the document says Your exported message data will be streamed continuously back down to you in a gzipped CSV format.. I thought that in this case, the returned data might be able to be ungzipped with Utilities.ungzip, and the ungzipped data might be able to be parsed with Utilities.parseCsv.
3. Using your 3 requests, how about the following sample script?
function exportjob2() {
var accessToken = "###your access token###"; // Please set your access token.
// 1st request: This is from https://stackoverflow.com/a/73032528
var url1 = 'https://api.intercom.io/export/content/data';
var options1 = {
"method": "post",
"headers": {
"Authorization": "Bearer " + accessToken,
"Accept": "application/json",
},
"contentType": "application/json",
"payload": JSON.stringify({
"created_at_after": 1654041600,
"created_at_before": 1656547200
})
}
var response1 = UrlFetchApp.fetch(url1, options1);
var { job_identifier } = JSON.parse(response1.getContentText());
// 2nd request <--- Modified
var url2 = 'https://api.intercom.io/export/content/data/' + job_identifier;
var options2 = {
"headers": {
"Authorization": "Bearer " + accessToken,
"Accept": "application/json",
},
}
var response2 = UrlFetchApp.fetch(url2, options2);
// console.log(response2.getContentText()); // for debug.
var { download_url, status } = JSON.parse(response2.getContentText());
while (status == "pending") {
Utilities.sleep(5000); // Please adjust this value. The current wait time is 5 seconds.
response2 = UrlFetchApp.fetch(url2, options2);
// console.log(response2.getContentText()); // for debug.
var obj = JSON.parse(response2.getContentText());
status = obj.status;
download_url = obj.download_url;
}
if (!download_url) {
throw new Error("download_url has no value.");
}
// 3rd request
var options3 = {
"headers": {
"Authorization": "Bearer " + accessToken,
"Accept": "application/octet-stream",
},
}
var response3 = UrlFetchApp.fetch(download_url, options3);
var blob = response3.getBlob().setContentType("application/zip");
var csvs = Utilities.unzip(blob);
// Create a new Spreadsheet and put the CSV data to the 1st tab.
var ss = SpreadsheetApp.create("sample Spreadsheet");
csvs.forEach((b, i) => {
var ar = Utilities.parseCsv(b.getDataAsString());
var sheet = i == 0 ? ss.getSheets()[i] : ss.insertSheet("sample" + (i + 1));
sheet.getRange(1, 1, ar.length, ar[0].length).setValues(ar);
});
}
I'm not sure whether the downloaded the gzip file has the correct mimeType. So I added the mimeType like var blob = response3.getBlob().setContentType("application/x-gzip").
Note:
When this script is run, the flow of your showing question is done. But, I cannot test this API because I have no account. So, when an error occurs, please check each value and your access token again. And, please provide the error message. By this, I would like to confirm it.
I thought that the value of download_url returned from the 1st request might be the same with the value of download_url returned from 2nd request. But, I cannot test this API because I have no account. So, please check it, and when my understanding is correct, you can modify the above script.
This sample script creates a new Spreadsheet. But, if you want to put the CSV data to the existing Spreadsheet, please modify the above script.
Reference:
fetch(url, params)
Related
This question already exists:
How to convert this curl example to google apps script / javascript? [duplicate]
Closed 1 year ago.
So this part works:
function authenticateToggl()
{
url = "https://api.track.toggl.com/api/v8/me";
//Put it all together.
var options =
{
'method' : 'get',
'headers' : {"Authorization": 'Basic ' + Utilities.base64Encode('email#gmail.com:mypassword')},
};
//Make Login call to When I work.
var responseGetPlan = UrlFetchApp.fetch(url, options);
var strResponseGetPlan = responseGetPlan.getContentText();
Logger.log('Get Plan Response: ' + strResponseGetPlan); //Log response.
var parsedData = JSON.parse(strResponseGetPlan); //Parse into JSON format.
var strId = parsedData.data.api_token;
return strId;
}
That returned me a string api token, which I can use in api curl calls like this:
curl -v -u c8f4c3TOKENe5a580bfeab:api_token -X GET https://api.track.toggl.com/api/v8/workspaces/1001455/projects
Doing that I appropriately got back my list of projects per the api docs.
But when I tried to translate that into google apps script, it fails. I'm sure it is that my header is formatted wrong?
function nextStep(){
UrlFetchApp.fetch('https://api.track.toggl.com/api/v8/workspaces/1001455/projects', {//
'headers': {'Authorization': "Basic c8f4c3TOKENe5a580bfeab:api_token"},
'method':'get'
});
}
I get back a 403 authentication error. Does the word basic not apply here? using the same url, method, and token.
It seems you need to convert your string token to base64 encoding,
function nextStep(){
//API Key requires base64 encoding. Use Google Apps Scripts built in method
var base64token = Utilities.base64Encode("c8f4c3TOKENe5a580bfeab:api_token");
UrlFetchApp.fetch('https://api.track.toggl.com/api/v8/workspaces/1001455/projects', {
'headers': {'Authorization': "Basic "+base64token},
'method':'get'
});
}
Additional References:
Github - Google-Apps-Scripts-Toggl-Request
Github - toggl-appscript
I am not able to test my scripts.
If you can help with the code here - it is not working
As per documentation - https://developers.google.com/youtube/v3/docs/videos/insert
if (serviceYT.hasAccess()) {
url = 'https://youtube.googleapis.com/youtube/v3/videos?part=snippet%2CcontentDetails%2Cstatistics&mine=true&key='+ API_KEY;
var data;
data = '{"snippet":{"title":"testtitle","description":"testdes","categoryId":"19","tags":["asdf","sadfds"]},"status":{"privacyStatus":"public"}}';
var options = {
'headers': {
'Authorization': 'Bearer ' + serviceYT.getAccessToken()
,'Accept': 'application/json'
},
'contentType': 'application/json',
'method' : 'POST',
'payload' : data,
'muteHttpExceptions' : true
};
//execute and handle the response
var response = UrlFetchApp.fetch(url, options);
var responseCode = response.getResponseCode();
var result = JSON.parse(response.getContentText());
Logger.log(result);
}
My questions -
where to put the video?
Resolution of the error I am getting:
{error={message='status', code=400.0, errors=[{reason=unexpectedPart, domain=youtube.part, message='status', location=part, locationType=parameter}]}}
A youtube object (a empty video basically) is successfully added to the youtube which is visible via studio. its a empty video but the title, description, status etc... other things are correcty setup.
Any ideas on how to add the media or video??
Where in the payload the video blob has to be added?
I am not able to get it in the documentation.
https://developers.google.com/youtube/v3/docs/videos/insert
I believe your goal is as follows.
You want to upload a movie file to your YouTube channel with the title, description, and so on.
The movie file is put in your Google Drive.
You want to achieve this by directly requesting to the endpoint using UrlFetchApp.
In this case, how about the following modified script?
Modified script:
Before you use this script, please enable YouTube Data API v3 and add the scope of https://www.googleapis.com/auth/youtube. And please set the file ID of the movie file in your Google Drive to const fileId = "###";.
serviceYT.getAccessToken() is from your script.
function myFunction() {
const fileId = "###"; // Please set the file ID of movie file on the Google Drive.
const metadata = {
"snippet": { "title": "testtitle", "description": "testdes", "categoryId": "19", "tags": ["asdf", "sadfds"] },
"status": { "privacyStatus": "public" }
};
const url = 'https://www.googleapis.com/upload/youtube/v3/videos?part=snippet%2Cstatus';
const file = DriveApp.getFileById(fileId);
const boundary = "xxxxxxxxxx";
let data = "--" + boundary + "\r\n";
data += "Content-Type: application/json; charset=UTF-8\r\n\r\n";
data += JSON.stringify(metadata) + "\r\n";
data += "--" + boundary + "\r\n";
data += "Content-Type: " + file.getMimeType() + "\r\n\r\n";
const payload = Utilities.newBlob(data).getBytes().concat(file.getBlob().getBytes()).concat(Utilities.newBlob("\r\n--" + boundary + "--").getBytes());
const options = {
method: "post",
contentType: "multipart/form-data; boundary=" + boundary,
payload: payload,
headers: { 'Authorization': 'Bearer ' + serviceYT.getAccessToken() },
muteHttpExceptions: true,
};
const res = UrlFetchApp.fetch(url, options).getContentText();
console.log(res);
}
In your script, you are using the endpoint of url = 'https://youtube.googleapis.com/youtube/v3/videos?part=snippet%2CcontentDetails%2Cstatistics&mine=true&key='+ API_KEY;. Your data has the properties of snippet and status. In this case, it is required to use the endpoint of https://www.googleapis.com/upload/youtube/v3/videos?part=snippet%2Cstatus.
In order to upload the movie file, it is required to request with multipart/form-data.
When this script is run, the movie file on Google Drive is uploaded to YouTube.
Note:
In the current stage, even when "privacyStatus": "public" is used, the uploaded video is not public. About this, you can see it at the official document as follows. Ref
All videos uploaded via the videos.insert endpoint from unverified API projects created after 28 July 2020 will be restricted to private viewing mode. To lift this restriction, each API project must undergo an audit to verify compliance with the Terms of Service. Please see the API Revision History for more details.
When you use the YouTube API at Advanced Google services, you can use the following simple script. Ref
function myFunction2() {
const fileId = "###"; // Please set the file ID of movie file on the Google Drive.
const res = YouTube.Videos.insert({
"snippet": { "title": "testtitle", "description": "testdes", "categoryId": "19", "tags": ["asdf", "sadfds"] },
"status": { "privacyStatus": "public" }
}, ["snippet", "status"], DriveApp.getFileById(fileId).getBlob());
console.log(res);
}
References:
Videos: insert
Multipart-POST Request Using Google Apps Script
fetch(url, params)
Trying to use Google Apps Script and Google Photos API to add photos to Google Photos. Upload seems to work / returns a token, but then adding the photo to the library fails. The process consists of two steps: 1. Upload the photo data as described here, then 2. Add the photo to photo library as described here.
Step 1. works for me, as I get an upload token, but step 2 with source code below, throws an error, but my call has the one media item it needs.
{
"error": {
"code": 400,
"message": "Request must have at least one newMediaItem.",
"status": "INVALID_ARGUMENT"
}
}
My code after the upload step below. I have tried to stringify request body and have passed it to payload instead of body, but nothing worked. As the error seems specific enough, I've the feeling I'm just overlooking a tiny thing, but what??? Who has a working piece of code, preferably in apps script that I can have a look at?
requestHeader = {
"authorization": "Bearer " + photos.getAccessToken(),
"Content-Type": "application/json"
}
var requestBody = {
"newMediaItems": [
{
"description": "Photo description",
"simpleMediaItem": {
"fileName": fileName,
"uploadToken": uploadToken
}
}
]
}
var options = {
"muteHttpExceptions": true,
"method" : "post",
"headers": requestHeader,
"body" : requestBody
};
var response = UrlFetchApp.fetch("https://photoslibrary.googleapis.com/v1/mediaItems:batchCreate", options);
Logger.log("raw: " + response);
You want to add an image file to the album using Photo API with Google Apps Script.
You have already enabled Google Photo API at API console. And yout access token can be used for using the method of mediaItems.batchCreate.
If my understanding is correct, how about this answer? Please think of this as just one of several possible answers.
Usage:
1. Linking Cloud Platform Project to Google Apps Script Project:
About this, you can see the detail flow at here.
2. Add scope:
In this case, please addt the scope of https://www.googleapis.com/auth/photoslibrary to the manifest file (appsscript.json).
Although I think that from your question, above step 1 and 2 have already been done, I added them because I thought that this might be useful for other users.
3. Sample script:
In your script, I cannot see the detail of uploadToken. But in your question, I could confirm that you have alredy retrieved the value of uploadToken. So when you want to use your script for retrieving uploadToken, please replace uploadToken to yours. As the modification point of your script, 1. Include the album ID. 2. There is no body property of UrlFetchApp. 3. Please use JSON.stringify() to the payload.
function getUplaodToken_(imagefileId) {
var headers = {
"Authorization": "Bearer " + ScriptApp.getOAuthToken(),
"X-Goog-Upload-File-Name": "sampleFilename",
"X-Goog-Upload-Protocol": "raw",
};
var options = {
method: "post",
headers: headers,
contentType: "application/octet-stream",
payload: DriveApp.getFileById(imagefileId).getBlob()
};
var res = UrlFetchApp.fetch("https://photoslibrary.googleapis.com/v1/uploads", options);
return res.getContentText()
}
// Please run this.
function myFunction() {
var imagefileId = "###"; // Please set the file ID of the image file.
var albumId = "###"; // Please set the album ID.
var uploadToken = getUplaodToken_(imagefileId);
var requestHeader = {Authorization: "Bearer " + ScriptApp.getOAuthToken()};
var requestBody = {
"albumId": albumId,
"newMediaItems": [{
"description": "Photo description",
"simpleMediaItem": {
"fileName": "sampleName",
"uploadToken": uploadToken
}}]
};
var options = {
"muteHttpExceptions": true,
"method" : "post",
"headers": requestHeader,
"contentType": "application/json",
"payload" : JSON.stringify(requestBody)
};
var response = UrlFetchApp.fetch("https://photoslibrary.googleapis.com/v1/mediaItems:batchCreate", options);
Logger.log(response);
}
In this script, it supposes that the image file is put in Google Drive.
Note:
If the error of No permission to add media items to this album. occurs, please create the album by the script. The official document says as follows.
Media items can be created only within the albums created by your app.
In this case, please create new album by the following script, and please retrieve the album ID.
function createNewAlbum() {
var options = {
headers: {Authorization: "Bearer " + ScriptApp.getOAuthToken()},
payload: JSON.stringify({album: {title: "sample title"}}),
contentType: "application/json",
method: "post"
};
var res = UrlFetchApp.fetch("https://photoslibrary.googleapis.com/v1/albums", options);
Logger.log(res);
}
References:
Class UrlFetchApp
Upload media
Creating a media item
Method: mediaItems.batchCreate
If I misunderstood your question and this was not the direction you want, I apologize.
Found it! Not shown in the code I submitted, but still adding the fix, as it might help others making the same mistake I did. I directly assigned the response from UrlFetchApp to be the upload token, like so:
uploadToken = UrlFetchApp.fetch("https://photoslibrary.googleapis.com/v1/uploads", options);
but needed to call .getContentText() on it to get it as string, like so:
uploadToken = UrlFetchApp.fetch("https://photoslibrary.googleapis.com/v1/uploads", options).getContentText();
I have a blob data. I would like to upload this to a cell in google sheet using google sheet API v4.
I have looked at the documentation here.
https://developers.google.com/sheets/api/guides/values
I have also looked at SO questions here.
Insert image into Google Sheets cell using Google Sheets API
result = service.spreadsheets().values().update(
spreadsheetId=spreadsheet_id, range=range_name,
valueInputOption=value_input_option, body=body).execute()
I am not seeing any service described to insert the blob as the image. Please help.
After suggestions below, we implemented the Webapp from here - Insert image into Google Sheets cell using Google Sheets API
This is how we are calling the web app from our python code
dropoff_signature = "ZGF0YT <clip > WVhSaA=="
web_app_url = "https://script.google.com/macros/s/A < clip > y/exec"
image_data = "data:image/png;base64," + dropoff_signature
data_to_post = {
'spreadsheetid' : spreadsheet_Id,
'sheetname' : 'Sheet1',
'imageurl' : image_data,
'column' : 5,
'row' : 5
}
encoded_data = urllib.urlencode(data_to_post)
# Send encoded data to application-2
url_result = urlfetch.fetch(web_app_url, encoded_data, method='POST')
We are seeing the following error in our Webapp.
result : 200 content : {"status":"error","defaultMessage":"Error retrieving image from URL or bad URL: data:image/png;base64, <clip> ","name":"Exception","fileName":"Code (Insert image into spreadsheet)","lineNumber":42,"stack":"\tat Code (Insert image into spreadsheet):42 (doPost)\n"}}
Can you please help?
Made this change. Still getting the bad URL error.
dropoff_signature = "ZGF0YTpp<clip>WVhSaA=="
web_app_url = "https://script.google.com/macros/s/A<clip>y/exec"
image_data = "data:image/png;base64," + dropoff_signature
data_to_post = {
'spreadsheetid' : spreadsheet_Id,
'sheetname' : 'Sheet1',
'imageurl' : image_data,
'column' : 5,
'row' : 5
}
# encoded_data = urllib.urlencode(data_to_post)
# Send encoded data to application-2
# url_result = urlfetch.fetch(web_app_url, encoded_data, method='POST')
url_result = urlfetch.fetch(url=web_app_url, payload=json.dumps(data_to_post), method='POST', headers={'Content-type': 'application/json'})
result : 200 content : {"status":"error","defaultMessage":"Error retrieving
image from URL or bad URL:
data:image/png;base64,Z<clip>A==","error":
{"message":"Error retrieving image from URL or bad URL: data:image/png;base64,Z<clip>A==","name":"Exception","fileName":"Code (Insert image into spreadsheet)","lineNumber":42,"stack":"\tat Code (Insert image into spreadsheet):42 (doPost)\n"}}
Here is the Webapp that we are using.
function doGet(e) {
return ContentService.createTextOutput("Authorization: Bearer " +
ScriptApp.getOAuthToken())
}
//
// Example curl command to insert an image:
//
// curl -L -d '{ "spreadsheetid": "1xNDWJXOekpBBV2hPseQwCRR8Qs4LcLOcSLDadVqDA0E","sheetname": "Sheet1", "imageurl": "https://www.google.com/images/srpr/logo3w.png", "column": 1, "row": 1 }' \
// -H "Authorization: Bearer <INSERT TOKEN RETURNED FROM GET HERE>" \
// -H 'Content-Type: application/json' \
// https://script.google.com/a/tillerhq.com/macros/s/AKfycbzjFgIrgCfZTvOHImuX54G90VuAgmyfz2cmaKjrsNFrTzcLpNk0/exec
//
var REQUIRED_PARAMS = [
'spreadsheetid', // example: "1xNDWJXOekpBBV2hPseQwCRR8Qs4LcLOcSLDadVqDA0E"
'sheetname', // Case-sensitive; example: "Sheet1"
'imageurl', // Can be an url such as "https://www.google.com/images/srpr/logo3w.png"
// or alternately "data:image/png;base64,iVBOR...<snip>...gg=="
'column', // 1-based (i.e. top left corner is column 1)
'row' // 1-based (i.e. top left corner is row 1)
];
function doPost(e) {
var result = {
status: "ok",
defaultMessage: "Image inserted."
}
try {
var params = (e.postData && e.postData.type == "application/x-www-form-urlencoded") ? e.parameter
: (e.postData && e.postData.type == "application/json") ? JSON.parse(e.postData.contents)
: undefined;
if (!params) throw new Error('Unsupported content-type, must be either application/x-www-form-urlencoded or application/json.');
REQUIRED_PARAMS.forEach(function(requiredParam) {
if (!params[requiredParam]) throw new Error('Missing required parameter ' + requiredParam);
});
SpreadsheetApp.openById(params.spreadsheetid).getSheetByName(params.sheetname).insertImage(params.imageurl, params.column, params.row);
} catch(e) {
console.error(e);
result.status = "error";
result.error = e;
result.defaultMessage = e.message;
}
return ContentService.createTextOutput(JSON.stringify(result))
.setMimeType(ContentService.MimeType.JSON)
}
Solution 1:
From your Python application you can use the following code to set an image with the IMAGE formula using Sheets API [1]. You would need to put your Spreadsheet ID and change the range where you want the image.
spreadsheet_id = '[SPREADSHEET-ID]'
range_name = 'D13'
service = build('sheets', 'v4', credentials=creds)
values = [
[
'=IMAGE("https://google.com","google")'
]
]
body = {
'values': values
}
result = service.spreadsheets().values().update(
spreadsheetId=spreadsheet_id, range=range_name,
valueInputOption='USER_ENTERED', body=body).execute()
Solution 2:
If instead you want to use the insertImage function [2] from Apps Script, to insert an over the grid image into the Sheets and not an image linked to a cell. You can deploy a Web App [3] with a doPost() function where you can do this and call the Web App from your Python application using the service account credentials. Also you need to deploy the Web App to execute as "user accessing the web app" so all the requests you do from the Web App will be made with the service account credentials.
Python script:
from google.oauth2 import service_account
import requests
import json
import google.auth.transport.requests
SCOPES = ['https://www.googleapis.com/auth/drive', 'https://www.googleapis.com/auth/spreadsheets']
SERVICE_ACCOUNT_FILE = 'service_account.json'
credentials = service_account.Credentials.from_service_account_file(
SERVICE_ACCOUNT_FILE, scopes=SCOPES)
delegated_credentials = credentials.with_subject('[USER-EMAIL-TO-IMPERSONATE]')
delegated_credentials.refresh(google.auth.transport.requests.Request())
token = delegated_credentials.token
headers = {'content-type': 'application/json', 'Authorization': 'Bearer ' + token}
url = '[WEB-APP-URL]'
data = {"file": '[blob]'}
response = requests.post(url, data=json.dumps(data), headers=headers)
Web App script:
function doPost(e) {
var ss = SpreadsheetApp.openById('[SPREADSHEET-ID]');
var sheet = ss.getSheets()[0];
var blob = DriveApp.getFileById("[IMAGE-ID]").getBlob();
sheet.insertImage(blob, 4, 14);
return ContentService.createTextOutput("Good");
}
I tested my code with an image obtained from Drive in the Web App. You could skip that part and send the blob directly from your Python application in the data payload.
To use a service account remember to give API access to all the scopes needed, you need to go to admin.google.com->security->Setting->Advance Settings->Manage API client access and use the service account client ID.
[1] https://developers.google.com/sheets/api/guides/values
[2] https://developers.google.com/apps-script/reference/spreadsheet/sheet#insertImage(BlobSource,Integer,Integer)
[3] https://developers.google.com/apps-script/guides/web
I'm trying to POST a file to a REST API via Google Apps Script. The idea is that I have a process that is creating copies of a Google Doc, and I want to be able to post those newly created Docs to a third party system.
I found in UrlFetchApp that I can send files. However, I'm having issues sending the correct header values.
My request looks like so:
var file = DriveApp.getFileById(fileId);
var body = {
"file": file.getAs(MimeType.PDF)
};
var headers = {
'Content-Disposition': 'attachment; filename="'+ file.getName() +'"',
'Content-Length': file.getSize()
};
My options when I call UrlFetchApp.fetch(url, options) looks like :
({
method:"POST",
headers:{
'Content-Disposition':"attachment; filename=\"My Merge Development_row_1.pdf\"",
'Content-Length':90665,
Authorization:"Bearer TOKEN"
},
contentType:"application/x-www-form-urlencoded",
muteHttpExceptions:true,
payload:{file:Blob}
})
The API that I'm sending the files to requires the 'Content-Length' header. But, when I try to set a value for 'Content-Length' header I get an Apps Script error, "Attribute provided with invalid value: Header:Content-Length". If I don't set the Content-Length header then the API responds that the Content-Length and file size don't match.
Any ideas on how I set the Content-Length header so I can POST the file?
There is an existing ticket highlighting that the documentation is not clear on this very issue
The solution is:
Move content length value from "content-Length" name/value pair in
headers to the advanced argument "contentLength"
So in your example your options should look like
({
method:"POST",
headers:{
'Content-Disposition':"attachment; filename=\"My Merge Development_row_1.pdf\"",
Authorization:"Bearer TOKEN"
},
contentLength: 90665,
contentType:"application/x-www-form-urlencoded",
muteHttpExceptions:true,
payload:{file:Blob}
})
EDIT: Added a full example function to get contentLength and blob shown below:
function testFilePost() {
var file = DriveApp.getFileById(doc_id).getAs(MimeType.PDF);
var headers = {
'Content-Disposition': 'attachment; filename="'+ file.getName() +'"',
};
var options =
{
"method" : "post",
"payload": file.getBytes(),
"headers": headers,
"contentLength": file.getBytes().length,
};
var result = JSON.parse(UrlFetchApp.fetch('http://httpbin.org/post', options).getContentText());
Logger.log(result);
}