HTML5 Filesystem API: downloading a sandboxed file? - html

I have some data in indexeddb files, and I'd like to allow my users to download that data to import into other applications. I can use Blobs to do this easily:
var blob = new Blob(['herp!'], {type: 'application/octet-stream'});
var iframe = document.createElement("iframe");
iframe.id = "myiframe";
iframe.style.display = "none";
iframe.src = window.webkitURL.createObjectURL(blob); // needs a revokeObjectURL
$('#dlplaceholder').append(iframe);
The correct type parameter to Blob is key: if I set it to text/plain, it won't download the file. If I have the iframe a height and width and a display type of block, I can see the data in the iframe.
However, I'll want to build one file out of multiple values from my database, and each of those values can be large, and I can have a lot of entries. I can concatenate those values in the Blob constructor, but I'm worried about having so much data in memory that I blow up.
So I decided to create a file in the sandboxed system and append each value to it, and use the file instead of the blob as the argument to createObjectURL. But when I do that, it
doesn't download; it behaves exactly as if I had used my Blob code with a type of text/plain. Here's the code:
var fname = 'mybadtest'; //UPDATE here's the problem: needs .bin file extension
var init_fs = function ( fs ) {
// first, write to the sandboxed file
fs.root.getFile(fname, {create: true, exclusive: true}, function(fileEntry) {
fileEntry.createWriter(function(fileWriter) {
fileWriter.onwriteend = function(e) {
// write to file completed. now use the file for download
fs.root.getFile(fname, {}, function(fileEntry) {
fileEntry.file(function(file) {
var iframe = document.createElement("iframe");
iframe.id = "myiframe";
iframe.style.display = "none";
iframe.src = window.webkitURL.createObjectURL(file) // needs revokeObjURL
$('#dlplaceholder').append(iframe);
}, errorHandler);
}, errorHandler);
};
var blob = new Blob(['derp!'], {type: 'application/octet-stream'});
fileWriter.write(blob);
}, errorHandler);
}, errorHandler);
};
window.webkitStorageInfo.requestQuota(PERSISTENT, mysz, function(grantedBytes) {
window.webkitRequestFileSystem(PERSISTENT, grantedBytes, init_fs, errorHandler);
}, err_function );
If I give the iframe a size and set the display to block, I can see the data I wrote to the file. So I know I got the file writing part right.
Any ideas on how I can give the file the right type so I can download it? I've been through the MDN docs, the HTML5 Rocks tuts, and Eric Bidelman's "Using the Filesystem API" book, but I don't see anything that addresses this situation. I'm using Chrome version 27.0.1453.110.
UPDATE: problem solved; see comment below.
UPDATED UPDATE: see Rob W's useful comment about an alternate approach below.

Related

Display a pdf file in the browser with sails

I have some documents in a directory and I want to show one embedded in the browser, I save the path of the document in a table and I can read the path from that table and download the document, but I can't figure out how to show the file in the browser.
I'm using the following code to send the file:
loadDocument: async function (req,res){
var SkipperDisk = require('skipper-disk');
var fileAdapter = SkipperDisk(/* optional opts */);
var fd = await Documents.find(
{
where: {id:'1'},
select: ['uploadFileFd']
}
).limit(1);
let uploadFileFd = fd[0]["uploadFileFd"];
var fileStream = fileAdapter.read(uploadFileFd);
fileStream.on('error', function (err){
return res.serverError(err);
});
res.contentType("application/pdf");
res.set("Content-disposition", "attachment; filename=" + "file"+ fd[0]["id"]+".pdf");
fileStream.pipe(res);
},
I want to call the function and load the pdf file in the browser, preferably without reloading all the page.
Clients browsers will download the pdf without trying to open the built-in PDF viewer (ie, Chrome) because of the Content-disposition: attachment header that you're sending - try using inline instead.
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition
res.setHeader('Content-type', 'application/pdf');
res.setHeader('Content-disposition', 'inline; filename="file' + fd[0]["id"] + '.pdf"');
I found a solution to my problem.
First I have to create a way to serve the static folder where the files are located, I found the answer here.
Then I modify the code to send the data encoded as base64 using 'base64-stream':
var readStream = fs.createReadStream(uploadFileFd);
readStream.on('open', function () {
readStream.pipe(new Base64Encode()).pipe(res);
});
readStream.on('error', function(err) {
res.end(err);
});
Finally I show the pdf file in the browser as follows:
.done(function(data){
var parent = $('embed#pdf').parent();
var newElement = "<embed src=data:application/pdf;base64,"+data+" id='pdf' width='100%' height='1200'>";
$('embed#pdf').remove();
parent.append(newElement);
})
Now I can display a pdf file in the browser embedded in my own page, thanks to all the people that try to help.

How to deal with Access-Control-Allow-Origin for myDrive files

The question arises from this note:
Someone here suggested using div's. The HTML requirement is very
skeletal. The 3D display is basically canvas, but it requires seven
three.js files, ten js files of my own making to exchange parameters
and other variables with the global variable and .dae collada files
for each of the 3D models you can see. If they could be linked in like
jQuery that might be the solution but I wonder about conflicts.
on Questions on extending GAS spreadsheet usefulness
principally, if they can be linked like jQuery part
The files to be linked are on myDrive. The thinking is that if I can copy the files into GAS editor, it seems as secure and more flexible to bring them into the html directly.
code.gs
function sendUrls(){
var folder = DriveApp.getFoldersByName("___Blazer").next();
var sub = folder.getFoldersByName("assembler").next();
var contents = sub.getFiles();
var file;
var data = []
while(contents.hasNext()) {
file = contents.next();
type = file.getName().split(".")[1];
url = file.getUrl();
data.push([type,url]);
}
return data;
}
html
google.script.run.withSuccessHandler(function (files) {
$.each(files,function(i,v){
if(v[0] === "js"){
$.get(v[1])
}
})
})
.sendUrls();
The first url opens the proper script file but the origin file is not recognisable to me.
I am not sure that this is a proper answer as it relies on cors-anywhere, viz:
function importFile(name){
var myUrl = 'http://glasier.hk/cors/tba.html';
var proxy = 'https://cors-anywhere.herokuapp.com/';
var finalURL = proxy + myUrl;
$.get(finalURL,function(data) {
$("body").append(data);
importNset();
})
}
function importNset(){
google.script.run
.withSuccessHandler(function (code) {
path = "https://api.myjson.com/bins/"+code;
$.get(path)
.done((data, textStatus, jqXHR) => {
nset = data;
cfig = nset.cfig;
start();
})
})
.sendCode();
}
var nset,cfig;
$(document).ready(function(){
importFile();
});
but it works, albeit on my machine, using my own website as the resource.
I used the Gas function in gas Shop to make the eight previously tested js files into the single tba.html script only file. I swapped the workshop specific script files for those needed for google.script.run but otherwise that was it. If I could find out how to cors-enable my site, I think I might be able to demonstrate how scripts might be imported to generate different views from the same TBA and spreadsheet interfaces.

Trying to create a text file using TS and HTML5

In my app I'm receiving a string which needs to be saved to the local machine. I', reading this guide (html5 filesystem tutorial). The problem I have to use TS ("typescript": "2.6.1") and looks like part of the API is not supported. The following line gives me two errors:
window.requestFileSystem(window.TEMPORARY, 1024*1024, onInitFs, this.errorHandler);
first:
[ts] Property 'requestFileSystem' does not exist on type 'Window'.
second:
[ts] Property 'TEMPORARY' does not exist on type 'Window'.
Any workaround or an updated documentation? PS I'm aware this is supported only in Chrome.
This API is currently only supported in Chrom and does not work in other browsers. If you are using Chrome however, you have to use the prefixed version of this function which is webkitRequestFileSystem:
var requestFileSystem = window.requestFileSystem || window.webkitRequestFileSystem;
The support also goes for window.TEMPORARY.
Now, if you want to create a file and write some content in it, you have to create a so-called writer object:
function onInitFs(fs) {
fs.root.getFile('my-file.txt', {create: true}, function(fileEntry) {
fileEntry.createWriter(function(fileWriter) {
fileWriter.onwriteend = function(e) {
...
};
fileWriter.onerror = function(e) {
...
};
var blob = new Blob(['Content that goes into the file'], {type: 'text/plain'});
fileWriter.write(blob);
}, errorHandler);
}, errorHandler);
}
requestFileSystem(window.TEMPORARY, 1024*1024, onInitFs, errorHandler);
For more information on the FileSystemFileEntry API check out this link.

How to Add a file to Google Drive via Docs Add On using App Script? [duplicate]

This question already has answers here:
Uploading Multiple Files to Google Drive with Google App Script
(5 answers)
Closed 6 years ago.
Here is my scenario. I've created an Add-On for Google Docs that acts as a video toolbox.
A feature I'm trying to add is the ability to record a video using the built in web cam (using videojs-recorder) and then link to that video within the doc. I've got the video part working, but not sure how to get the webm JS Blob converted into a Google Blob so I can create a file on the users Google Drive for sharing and linking.
Just to figure out how this might work this is what I've done so far without any luck.
CLIENT SIDE CODE
//event handler for video recording finish
vidrecorder.on('finishRecord', function()
{
// the blob object contains the recorded data that
// can be downloaded by the user, stored on server etc.
console.log('finished recording: ', vidrecorder.recordedData);
google.script.run.withSuccessHandler(function(){
console.log("winning");
}).saveBlob(vidrecorder.recordedData);
});
SERVER SIDE CODE
function saveBlob(blob) {
Logger.log("Uploaded %s of type %s and size %s.",
blob.name,
blob.size,
blob.type);
}
The errors I get seem to be related to serialization of the blob. But really the exceptions aren't very useful - and just point to some minimized code.
EDIT: Note that there is no FORM object involved here, hence no form POST, and no FileUpload objects, as others have indicated that this might be a duplicate, however it's slightly different in that we are getting a Blob object and need to save it to the server.
Thanks goes to Zig Mandel and Steve Webster who provided some insight from the G+ discussion regarding this.
I finally pieced together enough bits to get this working.
CLIENT CODE
vidrecorder.on('finishRecord', function()
{
// the blob object contains the recorded data that
// can be downloaded by the user, stored on server etc.
console.log('finished recording: ', vidrecorder.recordedData.video);
var blob = vidrecorder.recordedData.video;
var reader = new window.FileReader();
reader.readAsDataURL(blob);
reader.onloadend = function() {
b64Blob = reader.result;
google.script.run.withSuccessHandler(function(state){
console.log("winning: ", state);
}).saveB64Blob(b64Blob);
};
});
SERVER CODE
function saveB64Blob(b64Blob) {
var success = { success: false, url: null};
Logger.log("Got blob: %s", b64Blob);
try {
var blob = dataURItoBlob(b64Blob);
Logger.log("GBlob: %s", blob);
var file = DriveApp.createFile(blob);
file.setSharing(DriveApp.Access.ANYONE_WITH_LINK, DriveApp.Permission.COMMENT);
success = { success: true, url: file.getUrl() };
} catch (error) {
Logger.log("Error: %s", error);
}
return success;
}
function dataURItoBlob(dataURI) {
// convert base64/URLEncoded data component to raw binary data held in a string
var byteString;
if (dataURI.split(',')[0].indexOf('base64') >= 0)
byteString = Utilities.base64Decode(dataURI.split(',')[1]);
else
byteString = decodeURI(dataURI.split(',')[1]);
// separate out the mime component
var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];
return Utilities.newBlob(byteString, mimeString, "video.webm");
}

How would I read the file contents of a file in my app/extension's crx?

I want to include some files in my crx and then be able to read them in as data (into a string or Blob). How would I do this? Is there a way to use the FileSystem API for this?
chrome.runtime.getPackageDirectoryEntry was implemented on 2013-06-13, expected in Chrome 29:
Issue 177208: add read-only FileSystem API for access to packaged app/extension resources
Change: https://chromiumcodereview.appspot.com/16470003
Read file contents from crx via XHR is much more simple than FileSystem API:
var url = chrome.extension.getURL('the_file.txt'); // full url
var req = new XMLHttpRequest(); // read via XHR
req.open('GET', url);
req.onreadystatechange = function(e) {
if (req.readyState === 4 && req.status === 200) {
console.log(data);
} else {
// error
}
}
If you want to make the request in an injected context, you must have accessable resources declared in manifest.json first, list filename (support wildcards) in accessible resources entry.
"web_accessible_resources": [
"path_to_the_file.html",
"just_another_folder/*.txt"
]