I have the following code that I would like to use Drive v3 in App Script
function myFunction() {
let id = "<YOUR ODS FILE ID>";
let file = DriveApp.getFileById(id);
let fileBlob = file.getBlob();
newFile = {
name: "New File",
mimeType: "application/vnd.google-apps.spreadsheet"
}
try{
Drive.Files.create(newFile, fileBlob);
}catch(e){
Logger.log("Error");
Logger.log(e);
}
}
However by default the google app script only makes v2 available. The documentation does not seem so easy to do this migration directly in App Script.
How can I use Drive v3 in this code directly in the google app script?
I believe your goal as follows.
You want to convert the file of let file = DriveApp.getFileById(id) (ODS file from let id = "<YOUR ODS FILE ID>") as Google Spreadsheet.
You want to achieve this using Drive API v3 with UrlFetchApp of Google Apps Script.
Modification points:
Although, unfortunately, I'm not sure about the file size of let file = DriveApp.getFileById(id) from your question, I think that in your situation, the file content is required to be sent as multipart/form-data. Ref
At Advanced Google services, this multipart/form-data is achieved at the internal server side. But, when you want to achieve this using UrlFetchApp, it is required to create the request body.
When above points are reflected to the sample script, it becomes as follows.
Sample script:
In this case, Drive API is used. So please enable Drive API at Advanced Google services.
function myFunction() {
const fileId = "<YOUR ODS FILE ID>"; // Please set the file ID.
const metadata = {
name: "New File",
mimeType: MimeType.GOOGLE_SHEETS,
// parents: ["### folder ID ###"], // If you want to put the converted Spreadsheet to the specific folder, please use this.
};
const payload = {
metadata: Utilities.newBlob(JSON.stringify(metadata), "application/json"),
file: DriveApp.getFileById(fileId).getBlob(),
};
const options = {
method: "post",
payload: payload,
headers: { authorization: "Bearer " + ScriptApp.getOAuthToken() },
};
const url = "https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart"
const res = UrlFetchApp.fetch(url, options).getContentText();
console.log(res);
// DriveApp.createFile(blob) // This is used for automatically detecting the scope of "https://www.googleapis.com/auth/drive".
}
Note:
At uploadType=multipart method, the official document says as follows.
Use this upload type to quickly transfer a small file (5 MB or less) and metadata that describes the file, in a single request.
When you want to use the file more than 5 MB for this, please use the resumable upload. Ref
By the way, in your script, it seems that the ODF file on your Google Drive is used. In this case, you can also achieve your goal using the method of "Files: copy" in Drive API v3. The sample script is as follows.
function myFunction2() {
const fileId = "<YOUR ODS FILE ID>"; // Please set the file ID.
const url = `https://www.googleapis.com/drive/v3/files/${fileId}/copy`;
const params = {
method: "post",
headers: {authorization: `Bearer ${ScriptApp.getOAuthToken()}`},
contentType: "application/json",
payload: JSON.stringify({name: "New name", mimeType: MimeType.GOOGLE_SHEETS})
};
const res = UrlFetchApp.fetch(url, params);
console.log(res.getContentText())
}
References:
Files: create
Upload file data
fetch(url, params)
Files: copy
Related
I used below script to get the blob of abc.dat file which is generated via my Apps Script project. With the Drive service, it is easy.
Used oauthScope is https://www.googleapis.com/auth/drive.readonly
function ReadData() {
var files;
var folders = DriveApp.getFoldersByName("Holder");
if (folders.hasNext()) {
var folder = folders.next();
var files = folder.getFiles();
while (files.hasNext()){
file = files.next();
if(file.getName()=='abc.dat'){
var content = file.getBlob().getDataAsString();
return content;
}
}
}
return '';
}
In order to reduce the authentication scope, Now I am modifying the code to fully remove the https://www.googleapis.com/auth/drive.readonly oauthScope and use only the https://www.googleapis.com/auth/drive.file oauthScope.
Using the Drive api, I didn't found a direct way to get the blob of a file.
I used this below script to get the blob of a word document file. But it is not working for the .dat file with error fileNotExportable, Export only supports Docs Editors files, code 403
function getBlob(fileID, format){
var url = "https://www.googleapis.com/drive/v3/files/" + fileID + "/export?mimeType="+ format;
var blob = UrlFetchApp.fetch(url, {
method: "get",
headers: {"Authorization": "Bearer " + ScriptApp.getOAuthToken()},
muteHttpExceptions: true
}).getBlob();
return blob;
}
Found this article and tried changing the export with get in the url. The returning blob.getDataAsString() gives "Not found" now.
The mimeType I used when creating the abc.dat file is application/octet-stream .dat. But when check the generated file, its mimeType is text/plain. So I used the 'text/plain' as the input for 'format' parameter in getBlob function.
.dat file creation code :
var connectionsFile = {
title: filename,
mimetype: "application/octet-stream .dat",
parents: [{'id':folder.getId()}],
};
var blobData = Utilities.newBlob(contents);
file = Drive.Files.insert(connectionsFile,blobData);
}
How can I modify this code to get the blob from the file? or is there any other way around?
Thanks in advance!
I think that in your situation, it is required to use get method instead of export method. Because export method is used for Google Docs files (Document, Spreadsheet, Slides and so on). When your script is modified, how about the following modification?
Modified script:
function getBlob(fileID) {
var url = "https://www.googleapis.com/drive/v3/files/" + fileID + "?alt=media";
var blob = UrlFetchApp.fetch(url, {
method: "get",
headers: { "Authorization": "Bearer " + ScriptApp.getOAuthToken() },
muteHttpExceptions: true
}).getBlob();
return blob;
}
Reference:
Download files
I am trying to post any type of file using Google Apps Script to a web app but I am not familiar with doPost.
My code looks like this:
function call(){
var file = getFile('/2.html'); //gets a file from my Google Drive (no point in posting all the code here for that)
var options = {
'method' : 'post',
'payload' : {file : file}
};
var response = UrlFetchApp.fetch('https://script.google.com/macros/s/AKfycbx0aIU_XjOHPXh0P6y2dTMmvGpI6WAuac_Cq5BOGw7nDLRlodT-/exec',options)
Logger.log(response)
This seems to work, although I have read I need to base64 encode a file for it to work properly. That's something I don't really understand.
On the web app side I made a doPost(e) but no matter what I tried to do with 'e' I can't work out what kind of object it is and how to process it. All I want to do really is to save it to Google Drive.
You may be wondering why I am going to these lengths to post the file to disk via a web app when I could save it directly. The reason is I am trying to do the process asynchronously using UrlFetchApp.fetchAll() so I can speed up the process of writing many files to disk at once.
If variable file is of type File, Get Blob from File and base64 encode it:
var options = {
'method' : 'post',
'payload' : Utilities.base64EncodeWebSafe(file.getBlob().getBytes())
};
On receiver web -app, Decode it back:
const doPost = e => {
const file = DriveApp.createFile(
Utilities.newBlob(
Utilities.base64DecodeWebSafe(
e.postData.contents
)
)
)
}
Thanks to #TheMaster. I made some modifications to the code because it resulted in an error because a file name was needed. you'll need your own apps script web app url to go here [APPS SCRIPT WEB APP URL]:
const doPost = e => {
const file = DriveApp.createFile(
Utilities.newBlob(
Utilities.base64DecodeWebSafe(
e.parameter.file
),e.parameter.contentType,e.parameter.fileName
)
)
}
function sendFile(file){
var fileName = file.getName();
var contentType = file.getBlob().getContentType();
var url = "[APPS SCRIPT WEB APP URL]";
var payload = {
fileName: fileName,
contentType : contentType,
file: Utilities.base64EncodeWebSafe(file.getBlob().getBytes())
};
var options = {
method: "POST",
payload: payload,
muteHttpExceptions : true,
};
var res = UrlFetchApp.fetch(url, options).getContentText();
}
Trying to explore this with a very simple script but I'm getting an insufficient permissions error:
function mini(){
var gdriveId = "1hp8ncIG4Ww7FH8wi7HjJzzzzzzz";
var options = {
method: "GET",
headers: {
'Authorization': 'Bearer ' + ScriptApp.getOAuthToken()
},
}
var url = "https://www.googleapis.com/drive/v2/files/"+gdriveId+"/children";
var response = JSON.parse(UrlFetchApp.fetch( url, options).getContentText());
}
I tried enabling the v2 drive api in the advanced google services dropdown but that didn't work.
I believe your situation and goal as follows.
From gdriveId in your script, I thought that you want to retrieve the folder list in the root folder of gdriveId using the method of "Children: list" in Drive API v2.
You have already enabled Drive API at Advanced Google Services.
For this, how about this answer?
Modification points:
When your script is put to new GAS project and Drive API is enabled at Advanced Google Services, the scopes of the project is only https://www.googleapis.com/auth/script.external_request. The required scope can be automatically detected by the script editor. But, even when Drive API is only enabled, it seems that no scopes are added. I think that the reason of your issue is this.
Under above situation, if you want to retrieve the access token including the required scopes, in order to make the script editor automatically detect the scope of https://www.googleapis.com/auth/drive.readonly, for example, please put // DriveApp.getFiles() to the script as a comment line.
In this case, when you use the methods for other scopes in your script, those scopes can be automatically detected and added by the script editor.
Modified script 1:
When your script is modified, it becomes as follows.
function mini(){
var gdriveId = "1hp8ncIG4Ww7FH8wi7HjJzzzzzzz";
var options = {
method: "GET",
headers: {
'Authorization': 'Bearer ' + ScriptApp.getOAuthToken()
},
}
var url = "https://www.googleapis.com/drive/v2/files/"+gdriveId+"/children";
var response = JSON.parse(UrlFetchApp.fetch( url, options).getContentText());
}
// DriveApp.getFiles() // <--- Added this comment line. By this, the scope of https://www.googleapis.com/auth/drive.readonly is added.
Modified script 2:
When the method of Advanced Google service is used, the scope of https://www.googleapis.com/auth/drive is automatically added. By this, the following script works.
function test() {
var gdriveId = "1hp8ncIG4Ww7FH8wi7HjJzzzzzzz";
var res = Drive.Children.list(gdriveId);
console.log(res)
}
Other pattern:
From June 1, 2020, the files and folders in the shared Drive can be retrieved by Drive service. So you can also use the following script.
function myFunction() {
const getFolderList = (id, folders = []) => {
const f = DriveApp.getFolderById(id);
const fols = f.getFolders();
let temp = [];
while (fols.hasNext()) {
const fol = fols.next();
temp.push({name: fol.getName(), id: fol.getId(), parent: f.getName()});
}
if (temp.length > 0) {
folders.push(temp);
temp.forEach((e) => getFolderList(e.id, folders));
}
return folders.flat();
};
var gdriveId = "###"; // Please set the Drive ID.
const res = getFolderList(gdriveId);
console.log(res);
}
References:
Advanced Google services
Children: list of Drive API v2
Authorization Scopes
If you want to give permission to write with ScriptApp.getOAuthToken(), just add the following code in a commented out form and authorize it at runtime. If you don't do this, you'll only be able to download and browse.
//DriveApp.addFile("test");
Reference URL:https://00m.in/UeeOB
How I can manage the sharing-feature: "Disable Copying and downloading" without the advanced Drive Service?
Currently I solve it about:
function mySolveAboutAdvancedService(id) {
var file = Drive.Files.get(id);
file.labels.restricted = true;
Drive.Files.update(file, id);
}
Why I can change all settings but not this one without the advanced Drive Service?
Thanks
You want to achieve the following script without using Advanced Google services.
var file = Drive.Files.get(id);
file.labels.restricted = true;
Drive.Files.update(file, id);
You want to know the reason that "Disable Copying and downloading" cannot be achieved without using above script.
If my understanding is correct, how about this answer? Please think of this as just one of several possible answers.
Issue and workaround:
Drive API of Advanced Google services uses Drive API v2. In this case, labels.restricted is for Drive API v2, and also, the official document says as follows.
labels.restricted: Warning: This item is deprecated. Deprecated - use copyRequiresWriterPermission instead.
By this, when {labels: {restricted: true}} is used for Drive API v3, it cannot be used while no error occurs. But, when Drive API v2 is used with UrlFetchApp, {labels: {restricted: true}} can be still used. By this, your script using Drive API of Advanced Google service works.
In order to achieve your script without using Advanced Google services, please directly request to the endpoint of Drive API v3 (in this case, v3 is used.) with the request body of {copyRequiresWriterPermission: true} using UrlFetchApp. The sample script is as follows.
Sample script:
function mySolveAboutAdvancedService() {
var id = "###"; // Please set the file ID.
var url = "https://www.googleapis.com/drive/v3/files/" + id;
var params = {
method: "patch",
contentType: "application/json",
payload: JSON.stringify({copyRequiresWriterPermission: true}),
headers: {Authorization: "Bearer " + ScriptApp.getOAuthToken()}
};
var res = UrlFetchApp.fetch(url, params);
Logger.log(res.getContentText())
}
Note:
If you want to use Drive API v2 with UrlFetchApp, how about the following script? At Drive API v2, both {labels: {restricted: true}} and {copyRequiresWriterPermission: true} can be used.
function mySolveAboutAdvancedService() {
var id = "###"; // Please set the file ID.
var url = "https://www.googleapis.com/drive/v2/files/" + id;
var params = {
method: "put",
contentType: "application/json",
payload: JSON.stringify({copyRequiresWriterPermission: true}), // or payload: JSON.stringify({labels: {restricted: true}})
headers: {Authorization: "Bearer " + ScriptApp.getOAuthToken()}
};
var res = UrlFetchApp.fetch(url, params);
Logger.log(res.getContentText())
}
References:
Files of Drive API v2
Files of Drive API v3
Files: update of Drive API v3
If I misunderstood your question and this was not the direction you want, I apologize.
I can't find a way to change modification date on a file on Google Drive using Google Apps Script.
After I do a file.makeCopy(newFile, newFolder), I would like to make the modification time on the new copy the same as the original file.
I can't find documented way to do this...
You want to modify the modified time of the file on Google Drive.
You want to achieve this using Google Apps Script.
If my understanding is correct, how about this answer? Please think of this as just one of several possible answers.
In this answer, I used the method of Files: update in Drive API v3.
Sample script:
Before you use this script, please set the variables of newModifiedTime and fileId.
function myFunction() {
var newModifiedTime = "2019-01-01T00:00:00.000Z"; // Please set the new modified time.
var fileId = "###"; // Please set the file ID you want to modify the modified time.
var url = "https://www.googleapis.com/drive/v3/files/" + fileId;
var params = {
method: "patch",
headers: {Authorization: "Bearer " + ScriptApp.getOAuthToken()},
payload: JSON.stringify({modifiedTime: newModifiedTime}),
contentType: "application/json",
};
UrlFetchApp.fetch(url, params);
// DriveApp.createFile(blob);
}
Note:
The last line of // DriveApp.createFile(blob); is used for automatically adding the scope of https://www.googleapis.com/auth/drive and enabling Drive API.
In this case, please use the date string of RFC 3339 date-time. It's like 2019-01-01T00:00:00.000Z.
Although I tested this for Drive API v2, it seems that Drive API v2 cannot modify the modified date of the file.
References:
Files: update(Drive API v3)
Class UrlFetchApp
If I misunderstood your question and this was not the result you want, I apologize.
There is a way to modify the modifiedTime with the advanced Drive service (don't forget to turn it on in the project settings).
function setModifiedDate(originalFile /* DriveApp.File */, copyFile /* DriveApp.File */) {
/* based on the Drive API v2 */
Drive.Files.patch({
modifiedDate: originalFile.getLastUpdated().toISOString()
}, copyFile.getId(), {
setModifiedDate: true
})
}
This changes the lastUpdated() date and the Last Modified date on the Drive. The file I'm using is just an ascii text file.
function modifyFile() {
var file=DriveApp.getFileById("fileId");
Logger.log(file.getLastUpdated());
var content=file.getBlob().getDataAsString();
content+='\nThis is a new line';
file.setContent(content);
Logger.log(file.getLastUpdated());
}
This changes the Last Modified Date of a Google Doc
function modifyADocFile() {
var file=DriveApp.getFileById("Document Id");
var doc=DocumentApp.openById(file.getId());
var body=doc.getBody();
var bodytext=body.getText();
bodytext+='\nThis is a new line';
body.setText(bodytext);
doc.saveAndClose();
}