Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use FileAPI to download big generated data file

The JavaScript process generates a lot of data (200-300MB). I would like to save this data for further analysis but the best I found so far is saving using this example http://jsfiddle.net/c2U2T/ which is not an option for me, because it looks like it requires all the data being available before starting the downloading. But what I need is something like

var saver = new Saver();
saver.save(); // The Save As ... dialog appears
saver.onaccepted = function () { // user accepted saving
    for (var i = 0; i < 1000000; i++) {
        saver.write(Math.random());
    }
};

Of course, instead of the Math.random() will be some meaningful construction.

like image 962
Alex Netkachov Avatar asked May 13 '13 13:05

Alex Netkachov


2 Answers

@dader - I would build upon dader's example.

  1. Use HTML5 FileSystem API - but instead of writing to the file each and every line (more IO than it is worth), you can batch some of the lines in memory in a javascript object/array/string, and only write it to the file when they reach a certain threshold. You are thus appending to a local file as the process chugs (makes it easy to pause/restart/stop etc)
  2. Of note is the following, which is an example of how you can spawn the dialoge to request the amount of data that you would need (it sounds large). Tested in chrome.:

    navigator.persistentStorage.queryUsageAndQuota(
    function (usage, quota) {
      var availableSpace = quota - usage;
      var requestingQuota = args.size + usage;
      if (availableSpace >= args.size) {
        window.requestFileSystem(PERSISTENT, availableSpace, persistentStorageGranted, persistentStorageDenied);
      } else {
        navigator.persistentStorage.requestQuota(
            requestingQuota, function (grantedQuota) {
              window.requestFileSystem(PERSISTENT, grantedQuota - usage, persistentStorageGranted, persistentStorageDenied);
            }, errorCb
          );
      }
    }, errorCb);
    
  3. When you are done you can use Javascript to open a new window with the url of that blob object that you saved which you can retrieve via: fileEntry.toURL()

  4. OR - when it is done crunching you can just display that URL in an html link and then they could right click on it and do whatever Save Link As that they want.

But this is something that is new and cool that you can do entirely in the browser without needing to involve a server in any way at all. Side note, 200-300MB of data generated by a Javascript Process sounds absolutely huge... that would be a concern for whether you are storing the "right" data...

like image 167
Nick Sharp Avatar answered Oct 31 '22 00:10

Nick Sharp


What you actually are trying to do is a kind of streaming. I mean FileAPI is not suited for the task. Instead, I could suggest two options :

The first, using XHR facility, ie ajax, by splitting your data into several chunks which will sequencially be sent to the server, each chunk in its own request along with an id ( for identifying the stream ) and a position index ( for identifying the chunk position ). I won't recommend that, since it adds work to break up and reassemble data, and since there's a better solution.

The second way of achieving this is to use Websocket API. It allows you to send data sequentially to the server as it is generated. Following a usual stream API. I think you definitely need this.

This page may be a good place to start at : http://binaryjs.com/

That's all folks !

EDIT considering your comment :

I'm not sure to perfectly get your point though but, what about HTML5's FileSystem API ?

There are a couple examples here : http://www.html5rocks.com/en/tutorials/file/filesystem/ among which this sample that allows you to append data to an existant file. You can also create a new file, etc. :

function onInitFs(fs) {

  fs.root.getFile('log.txt', {create: false}, function(fileEntry) {

    // Create a FileWriter object for our FileEntry (log.txt).
    fileEntry.createWriter(function(fileWriter) {

      fileWriter.seek(fileWriter.length); // Start write position at EOF.

      // Create a new Blob and write it to log.txt.
      var blob = new Blob(['Hello World'], {type: 'text/plain'});

      fileWriter.write(blob);

    }, errorHandler);

  }, errorHandler);

}

EDIT 2 :

What you're trying to do is not possible using javascript as said on SO here. Tha author nonetheless suggest to use Java Applet to achieve needed behaviour.

To put it in a nutshell, HTML5 Filesystem API only provides a sandboxed filesystem, ie located in some hidden directory of the browser. So if you want to access the true filesystem, using java would be just fine considering your use case. I guess there is an interface between java and javascript here. But if you want to make your data only available from the browser ( constrained by same origin policy ), use FileSystem API.

like image 22
dader Avatar answered Oct 30 '22 22:10

dader