Edit on Google Docs without converting - google-drive-api

I'm integrating my system with Google Drive. Everything is working so far, but one thing. I cannot edit the uploaded Word documents without converting them to Google Docs first.
I've read here it's possible using a Chrome plugin:
https://support.google.com/docs/answer/6055139?hl=en
But that's not my goal. I'm storing the file's information on my database and then I just request the proper URL for editing and previewing. Previewing is working fine, but when I try the edit URL it says the file does not exist. If I convert the file (using Google Drive's interface) and pass the new ID it works. I don't want to convert the user's documents to Google Drive because they still use Word as their main editing software.
Is there a way to accomplish this?
This is how I'm doing right now:
public static File UploadFile(FileInfo fileInfo, Stream stream, string googleAccount)
{
var mimetype = GetValidMimetype(fileInfo.MimeType);
var parentFolder = GetParentFolder(fileInfo);
var file = new File { Title = fileInfo.Title, MimeType = mimetype, Parents = parentFolder };
var uploadRequest = _service.Files.Insert(file, stream, mimetype);
uploadRequest.Upload();
file = uploadRequest.ResponseBody;
ShareFileWith(file.Id, googleAccount);
return file;
}
This is the URL for editing (where {0} is the file ID):
https://docs.google.com/document/d/{0}/edit?usp=drivesdk
I know that in order to convert the file I just need to:
uploadRequest.Convert = true;
But again, that's not what I want. Is it possible?
Thanks!
EDIT
Just an update. Convert = true should've worked but it's not. I've raised an issue for that here https://github.com/google/google-api-dotnet-client/issues/712
Bottomline, it only works if I open the file on Google Docs and then use its Id...

Related

Export Google document as a string representation, then import it back (has to end up identical)

I'm looking for a way to programmatically export a Google Document, to be able to re-import it later, exactly as it was in the first place.
DriveApp.getFileById(file.id).makeCopy(filename) won't do the job since I need to send the document to an external server between export and re-import.
const file = DriveApp.getFileById(item.id);
const blob = file.getBlob(); // <- This line seems to convert the doc to pdf
const blobStr = blob.getDataAsString();
// Then later...
const newBlob = Utilities.newBlob(blobStr, blob.getContentType(), `COPY OF ${item.title}`);
const newFile = DriveApp.createFile(newBlob); // Create a new file from a blob
My problem here is that file.getBlob() converts the document to pdf. Which I don't want. I need it to still be a Google Document when I create the file.
Am I missing something ? maybe Google documents can't be represented as a string or byte at all...?
To give a little bit of context, I'm sending the document for translation to an external service. When translated strings come back, I need to map them back into the original document keeping the formatting and everything exactly as it was.

How do I download information stored in my Chrome extension?

I am developing a Chrome extension where the workflow looks like:
user browses the internet and can save links. I have a strong preferences to store all links locally instead of, say, having to talk to an external server.
user can then hit a button in the extension which generates and downloads a csv file of all the saved links so far
Two questions:
What is the appropriate way to store this data over multiple sessions?
What is the appropriate way of generating the file and prompting a download?
For 1, I plan on using chrome.storage.local.
For 2, it's unclear what the best way is. I'm considering writing the data to options.html or popup.html, then calling chrome.downloads to download that page, but it feels like a massive hack.
What is the correct way of doing 1 and 2?
Using chrome.storage.local is the right way here.
I am using this snippet right from the popup in order to save text/json/csv files:
/**
* #param data {String} what to save
* #param extension {String} file extension
*/
function saveFile(data, extension = 'json') {
const fileName = `export-file.${extension}`;
const textFileAsBlob = new Blob([data], {type: 'text/plain'});
const downloadLink = document.createElement('a');
downloadLink.download = fileName;
downloadLink.href = window.URL.createObjectURL(textFileAsBlob);
downloadLink.target = '_blank';
downloadLink.click();
return fileName;
}
It will save a file to disk. And it is not a hacky way.
Update for #2
Another way is to pass base64 URL to the downloads API:
chrome.downloads.download({url: 'data:image/gif;base64,SEVMTE8gV09STEQh', filename: 'test.txt'})

firebase Google Cloud storage download URL has folder name which becomes file name

We are using Firebase Google Cloud Storage Bucket to store our files.
When the logged in user wants the download the file kept inside certain folder
Eg: 123/admin/1469611803143/123.xlsx
The url generated will be
https://firebasestorage.googleapis.com/v0/b/MYWEBSITE.appspot.com/o/123%2Fadmin%2F1469611803143%2F123.xlsx?alt=media&token=whatever_alpa_numeric_token
As I download this file the file name will be 123%2Fadmin%2F1469611803143%2F123.xlsx
and not 123.xlsx
We have tried using download attribute to change the file name
but this did not change the file name to 123.xlsx
Please HELP
I'm pretty new with firebase but I achieved this with the following code :
var storageRef = firebase.storage().ref();
var child = storageRef.child("your path");
var uploadTask = child.put(<file>);
uploadTask.on(firebase.storage.TaskEvent.STATE_CHANGED,
function(snapshot){
// HANDLE TASK PROGRESS
},
function(error){
// HANDLE ERROR
},
function(){
// UPLOAD SUCCESSFULL
var newMetadata = {
contentDisposition : "attachment; filename=" + fileName
}
child.updateMetadata(newMetadata)
})
This is (fortunately or unfortunately) intended behavior. Technically, files in Firebase Storage are stored with the full path (so 123%2Fadmin%2F1469611803143%2F123.xlsx is actually the file name--the slashes and percent escaping are part of the name, and are only represented as path separators in the UI), which is how we get this behavior.
We're likely to modify how downloads work in the future (in that we'll truncate the name), but we've been busy fixing other bugs and polishing higher priority pieces.

Google Drive Folders/Files Created Using API Not Visible on Google Interface

This is rather strange. I used Google Drive API to create a folder in Google Drive and then uploaded a file there. I can retrieve the folder and file using the same API (the code is working fine in all respect). However, when I go to Google Drive Web interface, I can't seem to find the folder or file. The file also doesn't sync to my local drive. Is there a setting in API or elsewhere to set the "visibility" ON?
Thank you in advance.
I had the same issue. Turned out to be permissions. When the file is uploaded by the service account, the service account is set as the owner, and then you can't see the files from the Drive UI. I found the solution online (but can't seem to find it again...)
This is what I did...
It's C#, your question didn't specify. The code you're interested in is the permission stuff after you get the response body after the upload...
FilesResource.InsertMediaUpload request = service.Files.Insert(body, stream, "text/plain");
request.Upload();
//Start here...
Google.Apis.Drive.v2.Data.File file = request.ResponseBody;
Permission newPermission = new Permission();
newPermission.Value = "yourdriveaccount#domain.com";
newPermission.Type = "user";
newPermission.Role = "reader";
service.Permissions.Insert(newPermission, file.Id).Execute();
The file was visible on the Drive UI after this. I tried specifying "owner" for the role, like the api suggests, but I got and error saying that they're working on it. I haven't played around with the other setting yet, (I literary did this last night). Let me know if you have any luck with any other combinations on permissions.
Hope that helps
I had the same issue, but this got solved my using a list data type for parents parameter, eg: If one wants to create a folder under a folder("1TBymLMZXPGkouw-lTQ0EccN0CMb_yxUB") then the python code would look something like
drive_service = build('drive', 'v3', credentials=creds)
body={
'name':'generated_folder',
'parents':['1TBymLMZXPGkouw-lTQ0EccN0CMb_yxUB'],
'mimeType':'application/vnd.google-apps.folder'
}
doc = drive_service.files().create(body=body).execute()
While permission issue is the main cause of this problem. What I did to make the folders or files appear after I uploaded it with service account was to specify the parent folder. If you upload / create folder / files without parent folder ID, that object's owner will be the service account that you are using.
By specifying parent ID, it will use the inherited permissions.
Here's the code I use in php (google/apiclient)
$driveFile = new Google\Service\Drive\DriveFile();
$driveFile->name = $req->name;
$driveFile->mimeType = 'application/vnd.google-apps.folder';
$driveFile->parents = ['17SqMne7a27sKVviHcwPn87epV7vOwLko'];
$result = $service->files->create($driveFile);
When you create the folder, you should ensure you set a parent, such as 'root'. Without this, it will be not appear in 'My Drive' and only in Search (Have you tried searching in the UI?)
Since you have already created the folder, you can update the file and give it the parent root as well.
You can test it out using the Parents insert 'Try it now' example.
Put your Folders ID in the fileId box, then in the request body, add root in the ID field.
private void SetFilePermission(string fileId)
{
Permission adminPermission = new Permission
{
EmailAddress = "test#gmail.com", // email address of drive where
//you want to see files
Type = "user",
Role = "owner"
};
var permissionRequest = _driveService.Permissions.Create(adminPermission, fileId);
permissionRequest.TransferOwnership = true; // to make owner (important)
permissionRequest.Execute();
Permission globalPermission = new Permission
{
Type = "anyone",
Role = "reader"
};
var globalpermissionRequest = _driveService.Permissions.Create(globalPermission, fileId);
globalpermissionRequest.Execute();
}

Open a file from Google Sheets script

I've had a Google Sheets script running for some time (a year) that needs to read an HTML file from it's Google Drive directory. The code to open the file looks like this:
var myHtmlFile = UrlFetchApp.fetch("https://googledrive.com/host/0B1m........JtZzQ/myfile.htm");
... and I could use the HTM file for further parsing.
Suddenly, the code above is throwing an error 404.
Has anything changed recently, stopping me from opening the file?
After a discussion with 'azawaza' (thanks for all the tips), I have finally solved this, so I'm posting the resolution in case others fall into this.
It looks like the construct
https://googledrive.com/host/{public_folder_id}/myfile.htm
in UrlFetchApp.fetch(url, true) can no longer be used. It gives error 404.
I was getting it from the following construct (for simplicity, assuming there is only one parent folder of my spreadsheet):
...
var myId = DocsList.getFileById(SpreadsheetApp.getActive().getId());
var folderId = myId.getParents()[0].getId();
var url = "https://googledrive.com/host/" + folderId + "/myfile.htm";
// url looks like: https://googledrive.com/host/0B1m....JtZzQ/myfile.htm"
var httpResp = UrlFetchApp.fetch(url,true); //throws 404 !!!
// now, parse 'httpResp'
The solution that worked for me, is to find the file directly using this construct (again, assuming there is only one file of given name) :
var htmlCont = DocsList.find("myfile.htm")[0].getContentAsString();
// now, parse htmlCont
I don't know why the 'old' solution no longer works. As I mentioned it worked for a year.
UPDATE (May 2015)
The 'DocsList' has been deprecated, a new construct:
var files = DriveApp.getFilesByName(myURL);
if (files.hasNext()) {
var htmlCont = files.next().getBlob().getDataAsString()
}
has to be used instead
I find it strange that it ever worked before! If it did, it was probably a bug - pretty sure it was never intended to work like that with "local" files... I have never seen it mentioned anywhere that UrlFetchApp.fetch() can fetch "local" files like that.
A simple fix would be to just use proper full url of the file:
var myHtmlFile = UrlFetchApp.fetch("https://googledrive.com/host/{public_folder_id}/myfile.htm");
That will ensure your code complies with the API and does not break next time Google changes something.