How can a Chrome extension save many files to a user-specified directory?

JavascriptGoogle ChromeGoogle Chrome-Extension

Javascript Problem Overview


I'm working on a Chrome extension to be used as an internal tool. Its required behavior is:

  1. As a page action, enable an address bar icon when looking at certain intranet pages.
  2. when the user clicks the icon, identify all files of a certain media type (say, .jpg) on the page, and
  3. silently save them all to a directory on the user's local drive.

This question has been asked before, but the answer then was "use NPAPI", and NPAPI is now derelict.

So, what is the currently available way to achieve this? The ones I've looked at are:

  • The chrome.FileSystem API --- but this does not save files in any user-accessible location. Instead the stored files are hidden behind obfuscated names in an undocumented directory. User requires that the files be stored under their original names in an accessible directory.
  • The HTML5 download attribute, by creating a data: URL and programmatically clicking it. This pops up a "save as..." dialog for each file, which is unacceptable when there are a hundred assets on a single page. User requires that the files be downloaded without further interaction beyond the single icon click.
  • The Chrome Download API, but that is only available in the beta and dev channels. User requires this extension work with mainstream Chrome.
  • Use the Native Messaging API by creating a small .exe that simply saves a file to disk, and then pass the .jpg as a blob to it. This seems very cumbersome and I am not even sure how to reliably pass large blobs to EXEs like that.

Is there another approach I can try?

Javascript Solutions


Solution 1 - Javascript

You've done quite a lot of research. Indeed, regular web pages cannot write to the user's filesystem without any plugins or extensions. Also, the HTML5 Filesystem API only provides access to a virtual filesystem, as you've observed.

However, you are confusing the chrome.fileSystem API with the HTML5 FileSystem API. Unlike the HTML FileSystem API, Chrome's fileSystem (app) API can directly write to the user's filesystem (e.g. ~/Documents or %USERPROFILE%\Documents), specified by the user.

This API is only available to Chrome apps, not extensions. This is not a problem, especially since you're developing an internal tool, because you can install the app and extension, and use message passing to communicate between the extension (page action) and app (file system access) (example).


About chrome.downloads: Since your extension is internal, you can probably force users to get on the beta/dev channel in order to use this API. The only limitation of this API is that the files will be saved in (a subdirectory of) the user-defined Downloads folder.

EDIT: The chrome.downloads API is now avaiable in all channels, including the stable branch (since Chrome 31).

Solution 2 - Javascript

I am afraid that you have done your homework, meaning you looked at all possible alternatives.

The best way to achieve exactly what you want, would be (as you mentioned) using a supporting native app and communicating through Native Messaging. BTW, since bandwidth is rarely a problem on intranets, you might find it simpler to pass the resources (e.g. images) URLs and have the app download and save them.
(Yes, it will be more cumbersome than simply developing an extension, but one's got to do what they've got to do, right ?)

On the other hand, if you are willing to sacrifice a little bit of user's experience over simplicity of development, I suggest combining the HTML5 goodies (that allow you to create and download a file locally) with a JS zipping library (e.g. JSZip), so the user only has to download a single zip file (and is only prompted once). BTW, if the user wishes, he/she can choose to always download the files without prompting (but you knew that already).

Solution 3 - Javascript

Use the Native Messaging App idea.

The native app is cumbersome and a pain to write because documentation is poor, and unless you get the JSON formatting exactly correct on both ends you will not see anything in a console because stdin and stdout are taken over.

But, you will be happier when it is done because you can use standard tools (e.g., Windows Explorer, a hex editor, TeamViewer...) to view, move, and delete files, and otherwise see what is going on. Chrome's sand-boxed file system works, but seems to now be a dead-end (no other browsers have picked it up). No one is likely to develop third-party tools for it. Of course you probably don't need tools once everything is working, but until then, debugging is a nightmare because you need to write code (and quite a lot of code) just to track what files are in what directories, file versions, remaining disk space...

Solution 4 - Javascript

Another solution for internal (or may be non-internal) usage is to connect to a websocket server, local or remote.

You can put it in both background.js or content.js (use wss:// for https://)

var ws = new WebSocket('ws://echo.websocket.org');
// var ws = new WebSocket('ws://127.0.0.1:9000');
ws.onmessage = function(res) {
    console.log('received data:', res.data);
};
ws.onopen = function() {
    ws.send('hello');
};

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionCrashworksView Question on Stackoverflow
Solution 1 - JavascriptRob WView Answer on Stackoverflow
Solution 2 - JavascriptgkalpakView Answer on Stackoverflow
Solution 3 - JavascriptDaveWalleyView Answer on Stackoverflow
Solution 4 - JavascriptimanView Answer on Stackoverflow