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.
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...
I am trying to create buttons based on the contents of a text file. I have pieced together code I found online to almost get the results I want. I want it to automatically load a predesignated file and create the buttons on page load. Currently it asks for the user to choose a file. I don't want that, I want it to just load a file with a hard coded name and generate the buttons.
I apologize in advance, I have zero JavaScript experience (please be gentile).
document.getElementById('file').onchange = function () {
var file = this.files[0];
var reader = new FileReader();
reader.onload = function (progressEvent) {
console.log(this.result);
// Parse file by line
var lines = this.result.split('\n');
// Create buttons
for (var line = 0; line < lines.length-1; line++) {
var btn = document.createElement("BUTTON");
var t = document.createTextNode(lines[line]);
btn.appendChild(t);
document.body.appendChild(btn);
}
};
reader.readAsText(file);
};
<input type="file" name="file" id="file">
Web pages are not allowed to read local files from the user's file system without explicit permission. They can however retrieve data from the server they were served from, as well as other servers.
If you're just editing a local web page and don't plan to move it to a server, your computer acts as both the client and the server. So you could use XMLHttpRequest instead of <input type=file> to access the file you need.
If you're loading your page via file:///, you'll need to put your file in the same directory as the web page or in a subdirectory (otherwise the same-origin policy will prevent you from accessing the file).
For my program I have to include huge index and data files in the program bundle. Because it is an universal app, I have included these files in a folder named "Data" within the "Shared" Project.
Now I try to read:
StorageFile file = await ApplicationData.Current.LocalFolder.GetFileAsync("Data/"+fileName);
Stream stream = (await file.OpenReadAsync()).AsStreamForRead();
BinaryReader reader = new BinaryReader(stream);
Windows.Storage.FileProperties.BasicProperties x = await file.GetBasicPropertiesAsync();
I get a System.ArgumentException "mscorlib.ni.dll" at the first line. What's wrong?
If somebody can help me and I get the file, I want to find the filesize. I hope, I can find this Information within the FileProperties (last line of code).
Then I want to set a FilePointer within that file and to read a defined number of binary data. Can I do that without reading the whole file in memory?
What you are trying to do is to access LocalFolder, which is not the same as Package.Current.InstalledLocation.
If you want to access files that are included with your package, you can do for example like this - by using URI schemes:
StorageFile file = await StorageFile.GetFileFromApplicationUriAsync(new Uri(#"ms-appx:///Data/"+fileName));
using (Stream stream = (await file.OpenReadAsync()).AsStreamForRead())
using (BinaryReader reader = new BinaryReader(stream))
{
Windows.Storage.FileProperties.BasicProperties x = await file.GetBasicPropertiesAsync();
}
or like this - by getting file from your Package, which you can access as StorageFolder - also pay attention here to use correct slashes (as it may be a source of exception):
StorageFile file = await Windows.ApplicationModel.Package.Current.InstalledLocation.GetFileAsync(#"Data\" + fileName);
using (Stream stream = (await file.OpenReadAsync()).AsStreamForRead())
using (BinaryReader reader = new BinaryReader(stream))
{
Windows.Storage.FileProperties.BasicProperties x = await file.GetBasicPropertiesAsync();
}
Note also that I've put your Stream and BinaryReader into using, as they are IDisposable and it's suitable to release those resources as they are no longer needed.
Note also that when your shared project has a name MySharedProject, you will have to modify the Path of above URI:
StorageFile file = await StorageFile.GetFileFromApplicationUriAsync(new Uri(#"ms-appx:///MySharedProject/Data/"+fileName));
or obtain the suitable StorageFolder:
StorageFile file = await Windows.ApplicationModel.Package.Current.InstalledLocation.GetFileAsync(#"MySharedProject\Data\" + fileName);
One remark after discussion:
When you add a file with .txt extension to your project, its Build Action by default is set to Content. But when you add file with .idx extension, as I've checked, its Build Action is set to None by default. To include those files in your package, change them to Content.
After Romasz has brought me to the right path, I can see the problem is quite different there.
My data files were involved in the correct place in the project, but Visual Studio does not bind all what you want.
In my project I need large data files to be firmly integrated into the program. These are between 13 KB and 41 MB in size and have file types .idx and .dat. These names are part of the problem.
What I know so far:
I may add .txt files with seemingly arbitrary size. Tested with 41 MB - no problem.
The same file with changed file type .idx is not added. The file is simply not included in the compiled project. No error message.
Of course I can rename the .idx files to another file type (tested with .id), but I want to know why idx files are treated differently. And why I got no error indication.
I have recently contributed some code to Moodle which uses some of the capabilities of HTML5 to allow files to be uploaded in forms via drag and drop from the desktop (the core part of the code is here: https://github.com/moodle/moodle/blob/master/lib/form/dndupload.js for reference).
This is working well, except for when a user drags a folder / directory instead of a real file. Garbage is then uploaded to the server, but with the filename matching the folder.
What I am looking for is an easy and reliable way to detect the presence of a folder in the FileList object, so I can skip it (and probably return a friendly error message as well).
I've looked through the documentation on MDN, as well as a more general web search, but not turned up anything. I've also looked through the data in the Chrome developer tools and it appears that the 'type' of the File object is consistently set to "" for folders. However, I'm not quite convinced this is the most reliable, cross-browser detection method.
Does anyone have any better suggestions?
You cannot rely on file.type. A file without an extension will have a type of "". Save a text file with a .jpg extension and load it into a file control, and its type will display as image/jpeg. And, a folder named "someFolder.jpg" will also have its type as image/jpeg.
Instead, try to read the first byte of the file. If you are able to read the first byte, you have a file. If an error is thrown, you probably have a directory:
try {
await file.slice(0, 1).arrayBuffer();
// it's a file!
}
catch (err) {
// it's a directory!
}
If you are in the unfortunate position of supporting IE11, The file will not have the arrayBuffer method. You have to resort to the FileReader object:
// use this code if you support IE11
var reader = new FileReader();
reader.onload = function (e) {
// it's a file!
};
reader.onerror = function (e) {
// it's a directory!
};
reader.readAsArrayBuffer(file.slice(0, 1));
I also ran into this problem and below is my solution. Basically, I took have a two pronged approach:
(1) check whether the File object's size is large, and consider it to be a genuine file if it is over 1MB (I'm assuming folders themselves are never that large).
(2) If the File object is smaller than 1MB, then I read it using FileReader's 'readAsArrayBuffer' method. Successful reads call 'onload' and I believe this indicates the file object is a genuine file. Failed reads call 'onerror' and I consider it a directory. Here is the code:
var isLikelyFile = null;
if (f.size > 1048576){ isLikelyFile = false; }
else{
var reader = new FileReader();
reader.onload = function (result) { isLikelyFile = true; };
reader.onerror = function(){ isLikelyFile = false; };
reader.readAsArrayBuffer(f);
}
//wait for reader to finish : should be quick as file size is < 1MB ;-)
var interval = setInterval(function() {
if (isLikelyFile != null){
clearInterval(interval);
console.log('finished checking File object. isLikelyFile = ' + isLikelyFile);
}
}, 100);
I tested this in FF 26, Chrome 31, and Safari 6 and three browsers call 'onerror' when attempting to read directories. Let me know if anyone can think of a use case where this fails.
I proposing calling FileReader.readAsBinaryString on the File object. In Firefox, this will raise an Exception when the File is a Directory. I only do this if the File meets the conditions proposed by gilly3.
Please see my blog post at http://hs2n.wordpress.com/2012/08/13/detecting-folders-in-html-drop-area/ for more details.
Also, version 21 of Google Chrome now supports dropping folders. You can easily check if the dropped items are folders, and also read their contents.
Unfortunately, I donĀ“t have any (client-side) solution for older Chrome versions.
One other note is that type is "" for any file that has an unknown extension. Try uploading a file named test.blah and the type will be empty. AND... try dragging and dropping a folder named test.jpg - type will be set to "image/jpeg". To be 100% correct, you can't depend on type solely (or if at all, really).
In my testing, folders have always been of size 0 (on FF and Chrome on 64-bit Windows 7 and under Linux Mint (Ubuntu essentially). So, my folder check is just checking if size is 0 and it seems to work for me in our environment. We also don't want 0-byte files uploaded either so if it's 0 byte the message comes back as "Skipped - 0 bytes (or folder)"
FYI, this post will tell you how to use dataTransfer API in Chrome to detect file type: http://updates.html5rocks.com/2012/07/Drag-and-drop-a-folder-onto-Chrome-now-available
The best option is to use both the 'progress' and 'load' events on a FileReader instance.
var fr = new FileReader();
var type = '';
// Early terminate reading files.
fr.addEventListener('progress', function(e) {
console.log('progress - valid file');
fr.abort();
type = 'file';
});
// The whole file loads before a progress event happens.
fr.addEventListener('load', function(e) {
console.log('load - valid file');
type = 'file';
});
// Not a file. Possibly a directory.
fr.addEventListener('error', function(e) {
console.log('error - not a file or is not readable by the web browser');
});
fr.readAsArrayBuffer(thefile);
This fires the error handler when presented with a directory and most files will fire the progress handler after reading just a few KB. I've seen both events fire. Triggering abort() in the progress handler stops the FileReader from reading more data off disk into RAM. That allows for really large files to be dropped without reading all of the data of such files into RAM just to determine that they are files.
It may be tempting to say that if an error happens that the File is a directory. However, a number of scenarios exist where the File is unreadable by the web browser. It is safest to just report the error to the user and ignore the item.
An easy method is the following:
Check if the file's type is an empty string: type === ""
Check if the file's size is 0, 4096, or a multiple of it: size % 4096 === 0.
if (file.type === "" && file.size % 4096 === 0) {
// The file is a folder
} else {
// The file is not a folder
}
Note: Just by chance, there could be files without a file extension that have the size of some multiple of 4096. Even though this will not happen very often, be aware of it.
For reference, please see the great answer from user Marco Bonelli to a similar topic. This is just a short summary of it.