Google Drive API Upload to Parent Folder - google-apps-script

I have a JavaScript based Google Apps script that iterates through all files in Drive and converts MS files to Google format. I am using Drive API to upload and convert. Everything seems to work fine, except when I have a file in a Drive sub folder. When the file is uploaded back to Drive, I cannot define a parent folder, so it goes directly to the root.
I have a handle on the parent folder ID, but when I add "parents : folderID" to the JSON String, I do not see any change in the upload path.
This is my upload and convert function, is there any way to modify this to define a parent folder? I'm totally lost with the API... Can anyone assist?
var uploadFile = JSON.parse(
UrlFetchApp.fetch("https://www.googleapis.com/upload/drive/v2/files?uploadType=media&convert=true",
{
method: "POST",
contentType: officeFile.getMimeType(),
payload: officeFile.getBlob().getBytes(),
parents: ['id' : folderID], <<<<<<<<<<<<<<<<< this line
headers: {
"Authorization" : "Bearer " + ScriptApp.getOAuthToken()
},
muteHttpExceptions: true
}).getContentText());

With V3 of the API the format of the parents parameter has changed (didn't seem to be listed in the documentation which always makes things fun). Parents are no longer added as {id:} objects.
var folderId = '0BwwA4oUTeiV1TGRPeTVjaWRDY1E';
var fileMetadata = {
'name': 'photo.jpg',
'parents': [ folderId ]
};
var media = {
mimeType: 'image/jpeg',
body: fs.createReadStream('files/photo.jpg')
};
drive.files.create({
resource: fileMetadata,
media: media,
fields: 'id'
}, function(err, file) {
if(err) {
// Handle error
console.log(err);
} else {
console.log('File Id: ', file.id);
}
});
Full documentation

I use v2 API with curl and succeeded to upload file in parent folder by writing meta json file with below format.
{
"title": "35_20190702.csv",
"mimeType": "text/csv",
"description": "Uploaded From Curl",
"parents": [
{
"kind": "drive#parentReference",
"id": ${FOLDER_ID}
}
]
}

Related

How to use Resmush.it API from Apps Script (multi files upload)

I'm trying to use the resmush.it API from Apps Script and I'm struggling when making the request with UrlFetch.
From their documentation, I understand they need a files array, in a multipart/form-data request.
What I'm doing so far (the images come from a Google Slides)
function myFunction() {
const OPTIMIZER_URL = "http://api.resmush.it/ws.php"
let slides = SlidesApp.openById("1TUZSgG_XXX_ni6VhdbE5usRyMc");
let pages = slides.getSlides();
pages.forEach((page) => {
let images = page.getImages();
images.forEach(image => {
let payload = {
files: [{
file: image.getBlob()
}]
}
let options = {
method: "POST",
payload: payload,
muteHttpExceptions : true
}
let response = UrlFetchApp.fetch(OPTIMIZER_URL, options)
let jsonResponse = JSON.parse(response.getContentText())
console.log(jsonResponse);
})
})
}
I also tried with payload = { file: image.getBlob() } but no success.
I get this error everytime:
{ error: 400,
error_long: 'No file or url provided',
generator: 'reSmush.it rev.3.0.4.20210124' }
Can you see what is wrong ?
Thank you
Although I'm not sure whether from your provided document I could correctly understand the specification of the API you want to use, how about the following modified script?
When I saw your script, I'm worried that your image.getBlob() has no name. In that case, the data is not sent as files. I thought that this might be the reason for your issue. When my this guess is reflected in your script, it becomes as follows.
Modified script:
function myFunction() {
const OPTIMIZER_URL = "http://api.resmush.it/ws.php"
let slides = SlidesApp.openById("1TUZSgG_XXX_ni6VhdbE5usRyMc");
let pages = slides.getSlides();
pages.forEach((page) => {
let images = page.getImages();
images.forEach(image => {
let options = {
method: "POST",
payload: { files: image.getBlob().setName("sample") },
muteHttpExceptions: true
}
let response = UrlFetchApp.fetch(OPTIMIZER_URL, options)
console.log(response.getContentText());
})
})
}
In this modification, the request body uses { files: image.getBlob().setName("sample") }. I'm not sure about the detailed specification of the API you want to use. So, if the key of files cannot be used, please modify files to file and test it again.
And, if your API is required to use the unique names of the uploaded files for each request, please set the unique names by modifying sample of image.getBlob().setName("sample").
Reference:
fetch(url, params)

how to check if bucket object is already translated

I've uploaded file to oss and have object id, if bucket object is not yet translated then how to check derivatives info. with object id?
It's straightforward, just base64 encode your objectId, then call GET {urn}/manifest. If it returns a 404 http status code, then it means this URN hasn't got translated.
If your file is stored on BIM360/ACC, you will need to get derivative URN from the file's version tip. Please follow this tutorial, but find relationships.data.derivatives.data.id instead for the URN like the below for example.
https://forge.autodesk.com/en/docs/bim360/v1/tutorials/document-management/download-document/#step-4-find-the-storage-object-id-for-the-file
"derivatives": {
"data": {
"type": "derivatives",
"id": "dXJuOmFkc2sud2lwcHJvZDpmcy5maWxlOnZmLkVueWtrU3FjU0lPVTVYMGhRdy1mQUM_dmVyc2lvbj0x"
},
// ...
},
Node.js code sample tested with yiskang/forge-viewmodels-nodejs-svf2
const {
DerivativesApi
} = require('forge-apis');
const { getClient, getPublicToken } = require('./routes/common/oauth');
const derivativeApi = new DerivativesApi();
const urn = 'dXJuOmFkc2sub2JqZWN0czpvcy5vYmplY3Q6bXlidWNrZXQvdGVzdC5ydnQ';
getPublicToken().then(accessToken => {
derivativeApi.getManifest(urn, {}, null, accessToken).then(function (res) {
console.log(res.statusCode, res.statusMessage);
},
function (err) {
// When the urn hasn't got translated, it goes here
console.error('error', err.statusCode, err.statusMessage);
// if you want to redire page to some where, write your codes here
});
}, function (err) {
console.error(err);
});
ref: https://stackoverflow.com/a/70664111/7745569

Autodesk Forge: Translation Failed - File to bucket data: fileContent

I've been following this viewer walkthrough tutorial (node.js), for uploading and showing a file in the forge viewer.
I've been using angular to recreate the example, except instead of a user uploading a file, the file is hardcoded into the app from my assets folder for testing purposes.
The issue comes to when i try and translate the revit file into svf.
I know there isn't an issue with the revit file as i have used models.autodesk.io to check if all is good.
I can successfully create a bucket and post a job, but when calling the translation status to check if translation is completed, i receive this:
{
"type": "manifest",
"hasThumbnail": "false",
"status": "failed",
"progress": "complete",
"region": "US",
"urn": "dXJuOmFkc2sub2JqZWN0czpvcy5vYmplY3Q6OTA0ZmZmYmMtODI1Ni00OWY2LWI3YzYtNDI3MmM1ZDlmNDljL2RyYXBlLnJ2dA",
"version": "1.0",
"derivatives": [
{
"name": "drape.rvt",
"hasThumbnail": "false",
"status": "failed",
"progress": "complete",
"messages": [
{
"type": "error",
"code": "Revit-UnsupportedFileType",
"message": "<message>The file is not a Revit file or is not a supported version.</message>"
},
{
"type": "error",
"message": "Possibly recoverable warning exit code from extractor: -536870935",
"code": "TranslationWorker-RecoverableInternalFailure"
}
],
"outputType": "svf"
}
]
}
I'm pretty sure my code for translating a project is correct, i think the issue is coming from uploading the file to my bucket.
The body structure for the PUT request must contain the contents of the file.
Here is my code for loading and reading a file using XMLHttpRequest and FileReader
loadFile(bucketKey, accessToken) {
const reader: XMLHttpRequest = new XMLHttpRequest();
reader.open('GET', './assets/drape.rvt', true);
reader.responseType = 'blob';
reader.onloadend = (request) => {
const blob: Blob = reader.response;
console.log(blob); //returns BlobĀ {size: 372736, type: "text/xml"}
// Create file from blob
const modelFile: File = new File([blob], 'drape.rvt');
this.readFile(bucketKey, accessToken, modelFile);
};
reader.send();
}
readFile(bucketKey, accessToken, modelFile) {
const myReader: FileReader = new FileReader();
myReader.readAsArrayBuffer(modelFile);
myReader.onloadend = (e) => {
const arrayBuffer: ArrayBuffer = myReader.result as ArrayBuffer;
this.fileToBucket(bucketKey, accessToken, arrayBuffer);
};
}
And the put request:
fileToBucket(bucketKey, accessToken, fileContent) {
const encodedBucketKey = encodeURIComponent(bucketKey);
const encodedFileName = encodeURIComponent('drape.rvt');
const uploadURI = `https://developer.api.autodesk.com/oss/v2/buckets/${encodedBucketKey}/objects/${encodedFileName}`;
const options = {
headers: new HttpHeaders({
'Content-Type': 'application/octet-stream',
Authorization: 'Bearer ' + accessToken
})
};
const body = {
data: fileContent
};
this.http.put(uploadURI, body, options)
.subscribe(
success => {
// URL safe base64 encoding
const urn = btoa(success.objectId);
this.translateObject(accessToken, urn);
}, error => {
console.log('fileToBucket');
console.log(error);
});
}
I'm assuming that the file content is the issue, here is the equivalent using node.js for the tutorial: PUT request read file.
You can use a utility web app like https://oss-manager.autodesk.io/ to check if the file you previously uploaded is correct (by downloading it or trying to translate it to SVF through the app's UI) and upload the file using this utility and then try to translate it with your app. It can also be used to delete all the derivatives for a given file in your bucket.
That could help narrow down the issue.
It's also possible that the file was not correctly uploaded the first time (at a certain point when you were still testing things) and so the translation failed back then and now it won't try to translate the file again. You can force the translation to take place by adding x-ads-force to the POST Job request with value "true" - see https://forge.autodesk.com/en/docs/model-derivative/v2/reference/http/job-POST/

node.js / google drive sdk v3: access to webContentLink (and all image metadata)

I'm creating an image file in drive v3 successfully, but cannot retrieve the URI for the file; I'm expecting to find it in the success response_object.
I do find the file ID in the response_object and I can use that to construct a URI that displays the image:
https://drive.google.com/file/d/0Bw4DMtLCtPMkNERiTnpVNDdQV2c/view
I would like to have 'an official/correct' URI to hand to the google Cloud Vision API as an image source. I think I'm looking for the webContentLink v3-file metadata. I guess, more generally, I can't see how to get all the metadata (as described on https://developers.google.com/drive/v3/reference/files) for the file that I have just created.
var newFileMetadata = {
'name': unique_file_name,
description: options.multipart,
useContentAsIndexableText: false,
parents: [ file.id ]
};
var media = {
mimeType: 'image/jpg',
body: fs.createReadStream(options.src_dir + '/' + sourceFile),
viewersCanCopyContent: true,
writersCanShare: true
};
var request_object = drive.files.create({
auth: auth,
resource: newFileMetadata,
media: media,
fields: ['id']
},
function(err, response) {
if (err) {
console.log('drive.files.create error: %s %s', err, response);
return;
} else {
// file create success; get response
console.log('dump the response\n %s', JSON.stringify(response));
}
}
); // end of drive.files.create()
and the console output is (with cr for readability):
dump the request_object
{"uri":
{"protocol":"https:",
"slashes":true,
"auth":null,
"host":"www.googleapis.com",
"port":null,
"hostname":"www.googleapis.com",
"hash":null,
"search":"?fields=id&uploadType=multipart",
"query":"fields=id&uploadType=multipart",
"pathname":"/upload/drive/v3/files",
"path":"/upload/drive/v3/files?fields=id&uploadType=multipart",
"href":"https://www.googleapis.com/upload/drive/v3/files?fields=id&uploadType=multipart"},
"method":"POST",
"headers":{"Authorization":"Bearer ya29.GlxfB18oVtG6A3j7cXzSq17-RSmj9-2BlQm4zHD5sPzKkfhRM1FxlxUHc9mxWaka1N2fBiTJun-SYLB8ewuc63XVbUo01q0bS2FiS6iJTq9O1h9FQWfoO5r8E6z_6Q",
"User-Agent":"google-api-nodejs-client/0.10.0","host":"www.googleapis.com",
"transfer-encoding":"chunked",
"content-type":"multipart/related;
boundary=d6cef3f5-2246-5654-ac1f-d00561be5e8a"}
}
dump the response
{"id":"0Bw4DMtLCtPMkNERiTnpVNDdQV2c"}
to recap:
how do I get (in Node.js) a google Cloud Vision appropriate URI for a newly created google Drive v3 image file?
how do I get (in Node.js) the metadata for a newly created google Drive v3 image file?
Many thanks.
new information: I have tried a separate drive.files.get()
drive.files.get({
auth: auth,
fileId: response.id
},
function(err, response) {
if (err) {
console.log('drive.files.get error: %s %s', err, response);
return;
} else {
// file create success; get properties
console.log('get properties\n %s', JSON.stringify(response));
}
});
and I do receive some metadata (but not the big picture), console output, new upload:
get properties
{"kind":"drive#file","id":"0Bw4DMtLCtPMkWFdTMUVwY2tRZ2c","name":"2017-06-04T17:57:42.189Zlovelock.png","mimeType":"image/png"}
Use Files.get and specify in the 'field' parameter that you want to get webContentLink. It will return just that. Use the Try-it from the docs.
The complete list of metadata you can obtain is found in Files resource.
For testing purposes, pass your fileId here and run Execute.

Need to add a folder to a document library in SharePoint 2013 using REST - SharePoint Hosted app

I'm trying to add a folder to a document library using REST in a SharePoint 2013 SharePoint hosted app. It is hosted in an Office 365 developer site. I'm following the guidelines in MSDN here as well as using the excellent samples on dev.office.com (especially the REST Helper).
While capturing the request during debugging I can't see any problems with my request compared to the documentation and samples. I haven't had issues with any GET requests either.
Here is my request:
var datatext = "{'__metadata': {'type': 'SP.Folder'}, 'ServerRelativeUrl': '" + serverRelativeUrl + foldername + "'}";
$.ajax({
url : $appweburl + "/_api/web/folders",
type: "POST",
data: datatext,
headers: {
"accept": "application/json;odata=verbose",
"X-RequestDigest": $("#__REQUESTDIGEST").val(),
"content-type": "application/json;odata=verbose",
"content-length": datatext.length
},
success: function (data) {
success(data);
},
error: function (data) {
failure(data);
}
});
This generates an error "value does not fall within the expected range"
So this uses "data" instead of "body" because "body" was always generating a JSON Reader error. All of the samples indicate "body" but I found other sources that show this key should be "data" in a POST like this, and it resolves the JSON reader error. I ensure that ServerRelativeUrl leads with a "/" as well as filename.
Any help is greatly appreciated.
EDIT: To follow up on this, I was successful using JSOM only. It turns out that you must add a ListItem to the list and specify the content type to be a folder to achieve this. It works, but it makes me wonder if you have to do something similar in REST - that is rather than use the folder endpoints, do it like adding a listitem to a list. I have not confirmed it.
Usually the error Value does not fall within the expected range occurs since ServerRelativeUrl property was specified in incorrect format.
The following formats are supported:
[Document Library]/[New Folder] list/library relative url
/[Site]/[Web]/[Document Library]/[New Folder] - site collection
relative url
http(s)://[Server]/[Site]/[Web]/[Document Library]/[New Folder]
absolute url
How to create a Folder using SharePoint 2013 REST
Assume the following site structure:
`News` site (under `sites` managed path)
|
`Documents` library
|
`Archive` folder
Then the following examples demonstrate how to create a folder named 2010 in Documents library:
Example 1
createFolder(_spPageContextInfo.webAbsoluteUrl,'/sites/news/Documents/Archive/2011')
.done(function(data)
{
var folder = data.d;
console.log('Folder ' + folder.Name + ' has been created successfully');
})
.fail(
function(error){
console.log(JSON.stringify(error));
});
Example 2
createFolder(_spPageContextInfo.webAbsoluteUrl,'Documents/Archive/2010')
.done(function(data)
{
var folder = data.d;
console.log('Folder ' + folder.Name + ' has been created successfully');
})
.fail(
function(error){
console.log(JSON.stringify(error));
});
where
function executeRequest(url,method,headers,payload)
{
if (typeof headers == 'undefined' || headers == null){
headers = {};
}
headers["Accept"] = "application/json;odata=verbose";
if(method == "POST") {
headers["X-RequestDigest"] = $("#__REQUESTDIGEST").val();
}
var ajaxOptions =
{
url: url,
type: method,
contentType: "application/json;odata=verbose",
headers: headers
};
if(method == "POST") {
ajaxOptions.data = JSON.stringify(payload);
}
return $.ajax(ajaxOptions);
}
function createFolder(webUrl,folderUrl)
{
var url = webUrl + "/_api/web/folders";
var folderPayload = { '__metadata': { 'type': 'SP.Folder' }, 'ServerRelativeUrl': folderUrl};
return executeRequest(url,'POST',null,folderPayload);
}