Reputation: 5630
I have a need to create a copy of a Google Doc with a specific ID - not the "friendly" name like MyDocument
, but the name that makes it unique in the GoogleSphere - the one like 1x_tfTiA9-b5UwAf3k2fg6y6hyZSYQIvhSNn-saaDs4c
.
Here's the scenario why I would like to do this:
I have a newsletter which is in the form of a Google Doc. The newsletter is published on a website by embedding the document in a web page inside an <iframe>
element. Also published in the same way is a "large print" version of the newsletter that is the same, apart from the fact that the default font size is 24pt, rather than 11pt.
I am trying to automate the production of the large print version, but in such a way that the unique ID of the large print document doesn't change, so that the embedded <iframe>
for it still works.
I have experimented in the past with Google Apps Scripts routines for creating a deep copy of a document but the deep copy functions don't play nicely with images and tables, so I could never get a complete copy. If I could implement a "Save As" function, where the operand was an existing unique ID, I think this would do what I want.
Anyone know how I might do this?
Upvotes: 1
Views: 992
Reputation: 9872
I delved into this, attempting to set the id of the "large print" version of the file in a variety of ways:
via copy()
: var copiedFile = Drive.Files.copy(lpFile, spFile.id, options);
which yields the error:
Generated IDs are not currently supported for copy requests
via insert()
: var newFile = Drive.Files.insert(lpFile, doc.getBlob(), options);
which yields the error:
Generated IDs are not supported for Google Docs formats
via update()
: Drive.Files.update(lpFile, lpFile.id, doc.getBlob(), options);
This method successfully updates the "large print" file from the small print file. This particular line, however, uses the Document#getBlob()
method, which has issues with formatting and rich content from the Document
. In particular, as you mention, images and tables in are not preserved (among other things, like changes to the font, etc.). Compare pre with post
It seems that - if the appropriate method of exporting formatted byte content from the document can be found - the update()
method has the most promise. Note that the update()
method in the Apps Script client library requires a Blob
input (i.e. doc.getBlob().getBytes()
will not work), so the fundamental limitation may be the (lack of) support for rich format information in the produced Blob
data. With this in mind, I tried a couple methods for obtaining "formatted" Blob
data from the "small print" file:
Document#getAs(mimetype)
: Drive.Files.export(lpFile, lpFile.id, doc.getAs(<type>), options);
MimeType.GOOGLE_DOCS
: We're sorry, a server error occurred. Please wait a bit and try again.
MimeType.MICROSOFT_WORD
: Converting fromapplication/vnd.google-apps.document
toapplication/vnd.openxmlformats-officedocument.wordprocessingml.document
is not supported.
These errors do make sense, since the internal Google Docs MimeType is not exportable (you can't "download as" this filetype since the data is kept however Google wants to keep it), and the documentation for Document#getAs(mimeType)
indicates that only PDF export is supported by the Document Service. Indeed, attempting to coerce the Blob
from doc.getBlob()
with getAs(mimeType)
fails, with the error:
Converting from
application/pdf
toapplication/vnd.openxmlformats-officedocument.wordprocessingml.document
is not supported.
using DriveApp
to get the Blob
, rather than the Document Service:
Drive.Files.update(lpFile, lpFile.id, DriveApp.getFileById(smallPrintId).getBlob(), options);
This has the same issues as doc.getBlob()
, and likely uses the same internal methods.
using DriveApp#getAs
has the same errors as Document#getAs
Considering the limitation of the native Apps Script implementations, I then used the advanced service to obtain the Blob
data. This is a bit trickier, since the File
resource returned is not actually the file, but metadata about the file. Obtaining the Blob
with the REST API requires exporting the file to a desired MimeType
. We know from above that the PDF-formatted Blob
fails to be properly imported, since that is the format used by the above attempts. We also know that the Google Docs format is not exportable, so the only one left is MS Word's .docx
.
var blob = getBlobViaURL_(smallPrintId, MimeType.MICROSOFT_WORD);
Drive.Files.update(lpFile, lpFile.id, blob, options);
where getBlobViaURL_
implements the workaround from this SO question for the (still-broken) Drive.Files.export()
Apps Script method.
This method successfully updates the existing "large print" file with the exact content from the "small print" file - at least for my test document. Given that it involves downloading content instead of using the internal, already-present data available to the export methods, it will likely fail for larger files.
Testing Script:
function copyContentFromAtoB() {
var smallPrintId = "some id";
var largePrintId = "some other id";
// You must first enable the Drive "Advanced Service" before this will work.
// Get the file metadata of the to-be-updated file.
var lpFile = Drive.Files.get(largePrintId);
// View available options on the relevant Drive REST API pages.
var options = {
updateViewedDate: false,
};
// Ideally this would use Drive.Files.export, but there is a bug in the Apps Script
// client library's implementation: https://issuetracker.google.com/issues/36765129
var blob = getBlobViaURL_(smallPrintId, MimeType.MICROSOFT_WORD);
// Replace the contents of the large print version with that of the small print version.
Drive.Files.update(lpFile, lpFile.id, blob, options);
}
// Below function derived from https://stackoverflow.com/a/42925916/9337071
function getBlobViaURL_(id, mimeType) {
var url = "https://www.googleapis.com/drive/v2/files/"+id+"/export?mimeType="+ mimeType;
var resp = UrlFetchApp.fetch(url, {
headers: { Authorization: 'Bearer ' + ScriptApp.getOAuthToken()}
});
return resp.getBlob();
}
Upvotes: 1