1

I'm working on an app that needs to access a collection of external files. It's basically a music player. It works as-expected under a web server, but I also want it to work locally in the browser.

General overview:

  • index.htm (Small index file with markup, gather external js, css)
  • index.js (All the app code here)
  • dir.js (An array of file paths of all music files)
  • /AHX/ (location of the music files)
  • ahx.js (music player code)

The two main difficulties for this are:

  1. JavaScript cannot list directory contents, even if it is a child directory. Instead I express file paths as an array of strings.
  2. Loading external files only possible using XMLHttpRequest, which has security restrictions when running local/offline, but works in other environments (under HTTP or Chrome App, perhaps other platforms, not sure).

Oddly, in the latest Firefox, 2) is not an issue anymore. XMLHttpRequest works locally without disabling security.fileuri.strict_origin_policy. I'm not sure if that is standard behavior, but Chrome doesn't appear to allow it.

In any case, my current solution is generating a list of file-paths in a .js file (previously I used a txt file that required XHR), and using XMLHttpRequest to load the music files. This of course means I need to keep the folder structure and the file-path database in sync, using a shell script to rebuild the dir.js file.

XHR is only supposed to work over HTTP, so the app requires a web server. I want the app to work locally (and not just force the user to install as a Chrome App). So I am asking this question to find alternative methods of reading the data.

One alternative I tried is encoding all 1000 files in base64 strings and storing it in a JS object. This produces a rather large 8MB .js file. It doesn't appear to be slow to load, but I am assuming it isn't exactly efficient... Plus it is a pain to update/maintain.

localStorage, IndexedDB and Web SQL are all options, but there is no way to pre-populate the storage before the app runs. Perhaps utilize File API for a one-time setup of the storage database.

So back to my question: What are some solutions to accessing a large collection of binary files (200+ files, over 6MB etc) locally (i.e. opening the .html file directly)?

Edit: The app in question on GitHub, to clear up any confusion on my use case. But in general, I'm looking for ways to automatically read these music files from the app locally, without cross-origin errors. Also, here is the 'js-database' version. It stores all 1000 files in a 8MB js file likes so:

[{data:"base64-string-of-data-here",path:"original-path-here"}, ...]

In that way it bypasses the need for XHR.

Edit2: A solution using jszip and IndexedDB appears promising. It is not possible to load multiple files from multiple selected folders, but if the directory tree is zipped, jszip can access an array of all files in the format /FOLDER_HERE/FILE_HERE. The paths and binary data can then be imported into IndexedDB in a one-time setup. It also works fine on file:// URLs which is important.

It is also possible that jszip could be used to effectively build/update a large JSON structure of BASE64 strings of the contents, which doesn't require any setup by the user. Still need to be tested though.

bryc
  • 12,710
  • 6
  • 41
  • 61
  • Why don´t you use localStorage instead of files if it will be used locally? – juvian Feb 06 '17 at 16:39
  • when executed locally, will you app be wrapped inside an executable (with a local sever in it), or will it run as a html/js file straight in the browser? If it is the second case, you might need to implement extra security in your code against user modification of your source files.. – Kaddath Feb 06 '17 at 16:43
  • @Kaddath It would be the html/js directly. The app itself runs both as a Chrome App, and a normal webpage app. Why would I care if a user modifies the source? It is basically open source and runs completely offline. I'm not sure how executable JS apps run (electron/node?) but I'd prefer to keep the code as cross-target as possible, if that makes sense. Basically, if my app works when opening the HTML in Firefox, I want to keep that basic browser-functionality, and detect features that only work under other targets. – bryc Feb 06 '17 at 16:52
  • @juvian The data needs to be read from somewhere. If someone runs runs the .html for the first time, the localStorage data would have to already exist before the app starts. If the app can load data, why the extra step to insert into localStorage? – bryc Feb 06 '17 at 17:08
  • @bryc I don´t fully understand your use case. You say that you would want the app already have the data to begin with and that it runs completely offline. Where is the data stored then? As for encoding music files, there is a similar solution presented here : http://stackoverflow.com/questions/14074833/using-local-file-for-web-audio-api-in-javascript – juvian Feb 06 '17 at 17:28
  • @juvian Sorry, maybe I explained it too generally. I have a WebAudio API synthesizer/sequencer that plays some small MIDI-like files. These are external binary files 1-30kB. I have about 1000 of them. The goal is to make a jukebox that plays each song on a click. Files are stored in organized folders per author. Paths stored in JS object. They are retrieved via XMLHTTPRequest then turned into Uint8Array of bytes, bla bla. XMLHTTPRequest does not work locally. I am looking for alternative file-access than XMLHTTPRequest that works locally, hope that clears up. – bryc Feb 06 '17 at 20:21
  • @bryc if the midi files are static and never change, you could put it all as a big json and add it as a script to your html page such as songs = [{data:, author:}] – juvian Feb 07 '17 at 14:21

1 Answers1

0

don't take this as a definitive answer, this subject interests me too, if people around dont want to take time to elaborate an answer, please comment, it will be more useful than votes..

from what i learnt in javascript resources, consider that you cannot really bypass the security aspect of the question. Even open source, you should warn explicitly if you didn't take in account the security. People could distribute a modified version of the resources for example. It depends on what is done with the resources.

If this is for a player i recommend treating it as a data resource, not as a script resource, because of security (as long as you don't eval strings or such). JSON data could do the job here, but that would need to process the 1000 files. Not so hard to write a script that processes the files though.

HTML5 file API

I haven't used it yet, so i can just give you one or two links. With the downside that it restricts your player to recent browsers.

https://www.html5rocks.com/en/tutorials/file/dndfiles/

HTML5 File API read as text and binary

(i know, not an answer) use a library:

Except that in this case, this might be an answer, just because there is no real universal data retreivement in javascript. A good library would add that and a support for old browers.

Among these solutions, for example jQuery JSONP allows to do dynamical cross-domain GET requests. With data formatting (and not script), it is much safer to inject. But keep in mind that you should be aware in detail what your player does with the binary, and in which way it can be a risk.

http://api.jquery.com/jQuery.getJSON/

direct inclusion of script: not recommended

<script src="./sameFolderFile.js"></script>

As for direct script inclusion in a local folder structure, it actually works in local. IE says there is ActiveX content and asks for use permission, but it works in firefox and chrome. The tag can be dynamically added, but there is a big security risk here: malicious javascript code added in the resources will be executed. This can lead to risks for the users

Community
  • 1
  • 1
Kaddath
  • 5,933
  • 1
  • 9
  • 23
  • I have used the File API heavily, and it's quite good, but requires user to load files manually. However if used in combination with localStorage or IndexedDB, it could load from IndexedDB thereafter. Using XHR or Fetch is immediate, and yes, security-restricted on `file://` protocol. That said, the app is not cross-domain, if malicious code could be added to _sameFolderFile.js_, every file asset app could be compromised. There are no dependencies, it just required a webserver or chromeApp instance for XHR. I want to eliminate the need for a webserver. – bryc Feb 07 '17 at 03:44