Permalink
Browse files

initial commit

  • Loading branch information...
coolaj86 committed Jan 11, 2011
0 parents commit 0ba69c5d7a09431b62da3ce0696e09a2d70926cf
Showing with 945 additions and 0 deletions.
  1. +186 −0 README.md
  2. +199 −0 examples/file-reader-test.js
  3. +125 −0 examples/file-test.js
  4. BIN files/coolaj86-2010.jpg
  5. +5 −0 lib/file-error.js
  6. +39 −0 lib/file-list.js
  7. +297 −0 lib/file-reader.js
  8. +65 −0 lib/file.js
  9. +16 −0 lib/index.js
  10. +12 −0 package.json
  11. +1 −0 tests
186 README.md
@@ -0,0 +1,186 @@
+node-file-api
+====
+
+HTML5 FileAPI implemented in Node.JS.
+
+The goal here is to be able to use this in connection with `jsdom` to create test utilities, possibly scraping utilities, ultimately an API-driven browser written in Node.
+
+Usage
+====
+
+ npm install file-api
+
+ var FileAPI = require('file-api'),
+ File = FileAPI.File,
+ FileList = FileAPI.FileList,
+ FileReader = FileAPI.FileReader;
+
+API
+====
+
+Since `HTML5 FileAPI` has been described by the W3C (terse, technical) and Mozilla Developer Center (understandable, end-user-oriented) in detail, I'll just highlight the differences:
+
+ * `File` is not (yet) a subclass of `Blob`
+ * `FileError` and `FileException` are not yet implemented (they use `Error` instead)
+ * `blob: scheme` and remote `URI schemes` are not yet implemented
+
+File
+----
+
+In the browser, `File` has no constructor. In Node, it does.
+
+`node-mime` is used for extension-based automatic `ContentType` detection (uses `name` if available, or `path` if not)
+
+**File(StringUriPath)**
+
+ var file = new File("./files/myfile.txt");
+
+**File({ buffer: Node.Buffer })**
+
+ var file = new File({
+ name: "abc-song.txt", // required
+ type: "text/plain", // optional
+ buffer: new Buffer("abcdefg,hijklmnop, qrs, tuv, double-u, x, y and z")
+ });
+
+**File({ stream: Node.ReadStream })**
+
+ var file = new File({
+ name: "abc-song.txt", // required
+ type: "text/plain", // optional
+ stream: new EventEmitter() // a read stream (emits `error`, `data`, `end`)
+ });
+
+ process.nextTick(function () {
+ file.stream.emit('data', "abcdefg,hijklmnop, qrs, tuv, double-u, x, y and z");
+ file.stream.emit('end');
+ });
+
+**File(Object)**
+
+Only `path` is necessary
+
+ var file = new File({
+ path: "./files/myfile.txt", // path of file to read
+
+
+ buffer: Node.Buffer, // use this Buffer instead of reading file
+
+
+ stream: Node.ReadStream, // use this ReadStream instead of reading file
+
+
+ name: "SomeAwesomeFile.txt", // optional when using `path`
+ // must be supplied when using `Node.Buffer` or `Node.ReadStream`
+
+
+ type: "text/plain", // generated based on the extension of `name` or `path`
+
+
+ jsdom: true, // be DoM-like and immediately get `size` and `lastModifiedDate`
+ // [default: false]
+
+
+ async: true, // use `fs.stat` instead of `fs.statSync` for getting
+ // the `jsdom` info
+ // [default: false]
+
+
+ lastModifiedDate: fileStat.mtime.toISOString(),
+
+
+ size: fileStat.size || Buffer.length
+ );
+
+
+FileReader
+----
+
+**`FileReader.setNodeChunkedEncoding`** is a `non-standard` method which hints that the `FileReader` should chunk if possible
+
+I.E. the file will be chunk-transferred via http upload)
+
+The default is `false` since many webservers do not correctly implement the standard correctly,
+and hence do not accept chunked encoding from clients.
+
+**`FileReader.on`** is a `non-standard` alias of `addEventListener`
+
+**`EventTarget.target.nodeBufferResult`** is a `non-standard` property which is a `Node.Buffer` instance of the data.
+
+**`FileReader.on('data', fn)`** is a `non-standard` event which passes a `Node.Buffer` chunk each time the `progress` event is fired.
+
+ var fileReader = new FileReader();
+
+ fileReader.setNodeChunkedEncoding(true || false);
+ fileReader.readAsDataURL(new File('./files/my-file.txt'));
+
+ // non-standard alias of `addEventListener` listening to non-standard `data` event
+ fileReader.on('data', function (data) {
+ console.log("chunkSize:", data.length);
+ });
+
+ // `onload` as listener
+ fileReader.addEventListener('load', function (ev) {
+ console.log("dataUrlSize:", ev.target.result.length);
+ });
+
+ // `onloadend` as property
+ fileReader.onloadend', function () {
+ console.log("Success");
+ });
+
+FileList
+----
+
+The browser has no constructor for this. Node has two.
+
+**new FileList(f1, f2, ...)**
+
+ var fileList = new FileList(file1, file2, file3);
+
+**new FileList([f1, f2])**
+
+ var files = [
+ new File('./files/blob.bin'),
+ new File('./files/image.jpg'),
+ new File('./files/readme.txt')
+ ],
+ fileList;
+
+ fileList = new FileList(files);
+
+Formal Documentation
+====
+
+ * [W3C: FileAPI](http://dev.w3.org/2006/webapi/FileAPI)
+ * [W3C: Blob](http://dev.w3.org/2006/webapi/FileAPI/#dfn-Blob)
+ * [W3C: File](http://dev.w3.org/2006/webapi/FileAPI/#dfn-file)
+ * [W3C: FileList](http://dev.w3.org/2006/webapi/FileAPI/#dfn-filelist)
+ * [W3C: FileReader](http://dev.w3.org/2006/webapi/FileAPI/#dfn-filereader)
+ * [W3C: FileError](http://dev.w3.org/2006/webapi/FileAPI/#dfn-fileerror)
+ * [W3C: URI scheme](http://dev.w3.org/2006/webapi/FileAPI/#url)
+
+ * [MDC: Using files from web applications](https://developer.mozilla.org/en/using_files_from_web_applications)
+ * [MDC: File](https://developer.mozilla.org/en/DOM/File)
+ * [MDC: FileList](https://developer.mozilla.org/en/DOM/FileList)
+ * [MDC: FileReader](https://developer.mozilla.org/en/DOM/FileReader)
+
+TODO
+====
+
+ //
+ // TODO
+ //
+ // HTML5 File URI should be implemented
+ // will need non-ahr 301-handling requester not prevent circular dep
+ //
+ // File should be a subclass of Blob
+ //
+ // jsdom EventTarget // http://aptana.com/reference/html/api/EventTarget.html
+ // target.result
+ //
+ // jsdom ProgressEvent // http://www.w3.org/TR/progress-events/
+ // lengthComputable
+ // loaded
+ // total
+ // initProgressEvent
@@ -0,0 +1,199 @@
+(function () {
+ "use strict";
+
+ var assert = require('assert'),
+ sequence = require('futures/sequence')(),
+ fs = require('fs'),
+ FileApi = require('../lib'),
+ File = FileApi.File,
+ FileReader = FileApi.FileReader,
+ fileData = {
+ path: './files/coolaj86-2010.jpg',
+ name: 'coolaj86-2010.jpg',
+ type: 'image/jpeg'
+ },
+ fileStat = fs.statSync(fileData.path),
+ lastModifiedDate = fileStat.mtime.toISOString(),
+ fileBuffer = fs.readFileSync(fileData.path),
+ fileBase64 = fileBuffer.toString('base64'),
+ fileDataUrl = "data:image/jpeg;base64,",
+ file,
+ chunkedEncoding = (new Date().valueOf() % 2) ? true : false;
+
+
+
+ sequence
+
+ //
+ // Test DataURL conversion with different file load methods
+ //
+
+ // Give Path
+ // Give both onload and addEventListener
+ .then(function (next) {
+ var fileReader = new FileReader(),
+ onloaded,
+ size = 0,
+ count = 0;
+
+
+ // non-standard, node-only `data`
+ fileReader.addEventListener('data', function (data) {
+ count += 1;
+ size += data.length;
+ //console.log("no-chunk:", data.length);
+ });
+
+
+ // TODO Futures.join
+ fileReader.addEventListener('load', function (ev) {
+ onloaded = true;
+ assert.strictEqual(ev.target.result.length, fileDataUrl.length + fileBase64.length);
+ });
+ fileReader.onload = function (ev) {
+ assert.strictEqual(count, 1);
+ assert.ok(onloaded);
+ assert.strictEqual(ev.target.result.length, fileDataUrl.length + fileBase64.length);
+ next();
+ };
+
+ fileReader.setNodeChunkedEncoding(false);
+ fileReader.readAsDataURL(new File(fileData));
+ })
+
+
+ // Give Path
+ // Test chunked encoding
+ .then(function (next) {
+ var fileReader = new FileReader(),
+ size = 0,
+ count = 0;
+
+
+ // non-standard, node-only `data`
+ fileReader.addEventListener('data', function (data) {
+ count += 1;
+ size += data.length;
+ //console.log("chunk:", data.length);
+ });
+
+
+ fileReader.addEventListener('load', function (ev) {
+ assert.ok(count > 1);
+ assert.strictEqual(ev.target.result.length, fileDataUrl.length + fileBase64.length);
+ assert.strictEqual(ev.target.nodeBufferResult.length, fileBuffer.length);
+ assert.strictEqual(size, fileBuffer.length);
+ next();
+ });
+
+
+ fileReader.setNodeChunkedEncoding(true);
+ fileReader.readAsDataURL(new File(fileData));
+ })
+
+
+ // Give Buffer
+ .then(function (next) {
+ var fileReader = new FileReader();
+
+ fileReader.addEventListener('load', function (ev) {
+ assert.strictEqual(ev.target.result.length, fileDataUrl.length + fileBase64.length);
+ fileData.buffer = undefined;
+ delete fileData.buffer;
+ next();
+ });
+
+ fileData.buffer = fileBuffer;
+ // will not matter when the data is a buffer
+ fileReader.setNodeChunkedEncoding(chunkedEncoding);
+ fileReader.readAsDataURL(new File(fileData));
+ })
+
+
+ // Give Stream
+ .then(function (next) {
+ var fileReader = new FileReader(),
+ fileStream = fs.createReadStream(fileData.path),
+ size = 0;
+
+ // non-standard, node-only
+ fileReader.addEventListener('data', function (data) {
+ size += data.length;
+ });
+
+ fileReader.addEventListener('load', function (ev) {
+ assert.strictEqual(ev.target.result.length, fileDataUrl.length + fileBase64.length);
+ assert.strictEqual(ev.target.nodeBufferResult.length, fileBuffer.length);
+ assert.strictEqual(size, fileBuffer.length);
+ fileData.stream = undefined;
+ delete fileData.stream;
+ next();
+ });
+
+ fileData.stream = fileStream;
+ // will not matter when the data is a stream
+ fileReader.setNodeChunkedEncoding(chunkedEncoding);
+ fileReader.readAsDataURL(new File(fileData));
+ })
+
+
+
+ //
+ // Test readAsXyz methods
+ //
+
+ // ArrayBuffer
+ .then(function (next) {
+ var fileReader = new FileReader();
+
+ fileReader.addEventListener('load', function (ev) {
+ assert.strictEqual(ev.target.result.length, fileBuffer.length);
+ next();
+ });
+
+ fileData.buffer = fileBuffer;
+ fileReader.readAsArrayBuffer(new File(fileData));
+ })
+
+
+ // BinaryString
+ .then(function (next) {
+ var fileReader = new FileReader();
+
+ fileReader.addEventListener('load', function (ev) {
+ assert.strictEqual(ev.target.result.length, fileBuffer.toString('binary').length);
+ next();
+ });
+
+ fileData.buffer = fileBuffer;
+ fileReader.readAsBinaryString(new File(fileData));
+ })
+
+
+ // Text
+ .then(function (next) {
+ var fileReader = new FileReader();
+
+ fileReader.addEventListener('load', function (ev) {
+ assert.strictEqual(ev.target.result.length, fileBuffer.toString('utf8').length);
+ assert.strictEqual(ev.target.result.length, fileBuffer.toString('ascii').length);
+ next();
+ });
+
+ fileData.buffer = fileBuffer;
+ fileReader.readAsText(new File(fileData));
+ })
+
+
+ // DataURL well-tested in first section
+
+
+ // Pass
+ .then(function (next) {
+ console.log("No test failed assertions. Reached final test. All tests (ostensibly) passed.");
+ next();
+ });
+
+ if ('undefined' === typeof provide) { provide = function() {}; }
+ provide('html5-file-api-test');
+}());
Oops, something went wrong.

0 comments on commit 0ba69c5

Please sign in to comment.