simple-file-cache
v1.0.0
Published
In-memory cache to improve performance of repeated reads when using FileReader in the browser
Maintainers
Readme
simple-file-cache ReadMe
Introduction
simple-file-cache is a simple in-memory cache to improve performance when reading files using the HTML5 FileReader API. Performance improvements can be observed on all browsers, but are most dramatic with Chrome and Safari. For instance, these times to complete a moderately complex workload were observed:
- on desktop Chrome:
- 40 seconds (without simple-file-cache)
- 3 seconds (with simple-file-cache)
- on mobile Chrome (Samsung Galaxy S II):
- 200 seconds (without simple-file-cache)
- 20 seconds (with simple-file-cache)
- on desktop Safari:
- 15 seconds (without simple-file-cache)
- 1 second (with simple-file-cache)
- on desktop Firefox:
- 20 seconds (without simple-file-cache)
- 2 seconds (with simple-file-cache)
(lower times are better, obviously)
Dependencies
A browser environment with the asynchronous FileReader API.
Usage
simple-file-cache needs to be included in the web page before the code that wants to use it (it should be possible to use browserify to have it work with require() instead, though I have not tried).
Its entry point is the simpleFileCacheLoader() function, but its dependencies need to be injected, which is best done by using this sample code:
var prefixator = function (apiobject, memberFuncName, prefixArray) {
var result = undefined;
var daFunc = apiobject[memberFuncName];
if (daFunc) {
return {
prefix: '',
obj: daFunc
};
}
var capMFC = memberFuncName.charAt(0).toUpperCase() + memberFuncName.slice(1);
try {
prefixArray.forEach(function (element) {
daFunc = apiobject[element + capMFC];
if (daFunc) {
result = {
prefix: element,
obj: daFunc
};
throw {
name: 'BreakFromForEachException',
message: 'Exception to end array enumeration prematurely'
};
}
});
} catch (e) {
if (e.name !== 'BreakFromForEachException') { // rethrow
throw e;
}
// otherwise, just swallow our exception
}
// if none was found, this will return undefined
return result;
};
var installProperSlice = function (obj) {
return obj;
};
(function () {
var temp = function (obj) {
var cached_imp = prefixator(obj, "slice", ["webkit", "moz"]).obj;
obj.properSlice = function () {
return installProperSlice(cached_imp.apply(obj, arguments));
};
return obj;
};
installProperSlice = temp;
}());
var simpleFileCache = simpleFileCacheLoader({
EMPTY: 0,
LOADING: 1,
DONE: 2,
"blob": function (param1, param2) {
var new_blob = null;
// utterly silly impedance mismatch (I tried Function.prototype.bind.apply without success: TypeError)
if (param2 === undefined) {
new_blob = new window.Blob(param1);
} else {
new_blob = new window.Blob(param1, param2);
}
return installProperSlice(new_blob);
},
"fileReader": function () {
return new window.FileReader();
},
"byteArray": function (param) {
return new window.Uint8Array(param);
}});After that, all functionality is accessed through the simpleFileCache object:
simpleFileCache.createCachedCopy(blob)- parameters:
blob: an instance ofwindow.Blob
- result: a
CachedBlob, with the same contents as the parameter, that can be used with the functions fromsimpleFileCache.api
- parameters:
simpleFileCache.createUncachedCopy(cachedBlob)- parameters:
pseudoBlob: aCachedBlobobject generated using the functions fromsimpleFileCache.api
- result: an instance of
window.Blobwith the same contents as the parameter
- parameters:
simpleFileCache.api.blob(): same aswindow.Blob, except it has aproperSlicefunction instead ofsliceorwebkitSlice, etc., and it operates exclusively onCachedBlob,CachedUInt8Array, etc. objectssimpleFileCache.api.fileReader(): same aswindow.FileReader, except it operates exclusively onCachedBlob,CachedUInt8Array, etc. objectssimpleFileCache.api.byteArray(): same aswindow.Uint8Array, except it operates exclusively onCachedBlob,CachedUInt8Array, etc. objects
The principle is that simpleFileCache.api is meant to be passed to a module that itself expects FileReader to be injected, such that the module does not even have to know it is using the simple-file-cache API. You can see how this is used in the JPS project, which you can think of as the simple-file-cache example usage code.
In particular, it allows simple-file-cache to be easily taken out: just inject the FileReader API instead of the simple-file-cache API, and you're back to using FileReader directly. This will be useful the day browsers will finally fix this performance issue and render simple-file-cache unnecessary.
Note that you cannot mix the FileReader API with the simpleFileCache.api: for instance, you cannot use an instance generated by window.FileReader to read a CachedBlob: you must get a Blob from it first, using simpleFileCache.createUncachedCopy(cachedBlob).
Tests
Open test_harness/main.html with any browser (no support for FileReader is even required) to execute the self-tests.
Reporting Bugs
Email me if you find anything.
Support
simple-file-cache is free to use and modify (under the terms of the BSD license). If you find it useful, I request that you consider donating to the ACLU and the UNHCR, however.
