浏览代码

Added FileReader.

Szymon Kupś 8 年之前
父节点
当前提交
f3fea27fca

+ 3 - 1
packages/ckeditor5-upload/package.json

@@ -3,7 +3,9 @@
   "version": "0.0.1",
   "description": "Upload Feature for CKEditor 5.",
   "keywords": [],
-  "dependencies": {},
+  "dependencies": {
+	  "@ckeditor/ckeditor5-utils": "^0.8.0"
+  },
   "devDependencies": {
     "@ckeditor/ckeditor5-dev-lint": "^2.0.2",
     "gulp": "^3.9.1",

+ 94 - 0
packages/ckeditor5-upload/src/filereader.js

@@ -0,0 +1,94 @@
+/**
+ * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/**
+ * @module upload/filereader
+ */
+
+/* globals window */
+
+import ObservableMixin from '@ckeditor/ckeditor5-utils/src/observablemixin.js';
+import mix from '@ckeditor/ckeditor5-utils/src/mix.js';
+
+/**
+ * FileReader class - wrapper over native FileReader.
+ */
+export default class FileReader {
+	constructor() {
+		const reader = new window.FileReader();
+
+		/**
+		 * Instance of native FileReader.
+		 *
+		 * @private
+		 * @member {FileReader} #_reader
+		 */
+		this._reader = reader;
+
+		/**
+		 * Number of bytes loaded.
+		 *
+		 * @readonly
+		 * @observable
+		 * @member {Number} #loaded
+		 */
+		this.set( 'loaded', 0 );
+
+		reader.onprogress = evt => {
+			this.loaded = evt.loaded;
+		};
+	}
+
+	/**
+	 * Returns error that occurred during file reading.
+	 *
+	 * @returns {DOMError}
+	 */
+	get error() {
+		return this._reader.error;
+	}
+
+	/**
+	 * Reads provided file.
+	 *
+	 * @param {File} file
+	 * @returns {Promise} Returns a promise that will resolve with file's contents. Promise can be rejected in case of
+	 * error or when reading process is aborted.
+	 *
+	 *	const reader = new FileReader();
+	 *
+	 * reader.read( file )
+	 *
+	 */
+	read( file ) {
+		const reader = this._reader;
+		this.total = file.size;
+
+		return new Promise( ( resolve, reject ) => {
+			reader.onload = () => {
+				resolve( reader.result );
+			};
+
+			reader.onerror = () => {
+				reject( 'error' );
+			};
+
+			reader.onabort = () => {
+				reject( 'abort' );
+			};
+
+			this._reader.readAsDataURL( file );
+		} );
+	}
+
+	/**
+	 * Aborts file reader.
+	 */
+	abort() {
+		this._reader.abort();
+	}
+}
+
+mix( FileReader, ObservableMixin );

+ 125 - 0
packages/ckeditor5-upload/tests/filereader.js

@@ -0,0 +1,125 @@
+/**
+ * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/* globals window */
+
+import FileReader from '../src/filereader';
+import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils';
+
+describe( 'FileReader', () => {
+	let reader, fileMock, nativeReaderMock;
+	testUtils.createSinonSandbox();
+
+	beforeEach( () => {
+		testUtils.sinon.stub( window, 'FileReader', () => {
+			nativeReaderMock = new NativeReaderMock();
+
+			return nativeReaderMock;
+		} );
+
+		fileMock = {
+			size: 1024
+		};
+
+		reader = new FileReader();
+	} );
+
+	it( 'should initialize loaded property', () => {
+		expect( reader.loaded ).to.equal( 0 );
+	} );
+
+	it( 'should update loaded property', () => {
+		nativeReaderMock._mockProgress( 10 );
+		expect( reader.loaded ).to.equal( 10 );
+		nativeReaderMock._mockProgress( 20 );
+		expect( reader.loaded ).to.equal( 20 );
+		nativeReaderMock._mockProgress( 55 );
+		expect( reader.loaded ).to.equal( 55 );
+	} );
+
+	describe( 'read', () => {
+		it( 'should return a promise', () => {
+			expect( reader.read( fileMock ) ).to.be.instanceOf( Promise );
+		} );
+
+		it( 'should resolve on loading complete', () => {
+			const promise = reader.read( fileMock )
+				.then( result => {
+					expect( result ).to.equal( 'File contents.' );
+				} );
+
+			nativeReaderMock._mockLoading( 'File contents.' );
+
+			return promise;
+		} );
+
+		it( 'should reject on loading error', () => {
+			const promise = reader.read( fileMock )
+				.then( () => {
+					throw new Error( 'Reader should not resolve.' );
+				}, ( status ) => {
+					expect( status ).to.equal( 'error' );
+					expect( reader.error ).to.equal( 'Error during file reading.' );
+				} );
+
+			nativeReaderMock._mockError( 'Error during file reading.' );
+
+			return promise;
+		} );
+
+		it( 'should reject on loading abort', () => {
+			const promise = reader.read( fileMock )
+				.then( () => {
+					throw new Error( 'Reader should not resolve.' );
+				}, ( status ) => {
+					expect( status ).to.equal( 'abort' );
+				} );
+
+			nativeReaderMock._mockAbort();
+
+			return promise;
+		} );
+	} );
+
+	describe( 'abort', () => {
+		it( 'should allow to abort reading', () => {
+			const promise = reader.read( fileMock )
+				.then( () => {
+					throw new Error( 'Reader should not resolve.' );
+				}, ( status ) => {
+					expect( status ).to.equal( 'abort' );
+				} );
+
+			reader.abort();
+
+			return promise;
+		} );
+	} );
+
+	function NativeReaderMock() {
+		this.readAsDataURL = () => {};
+		this.abort = () => {
+			this._mockAbort();
+		};
+
+		this._mockLoading = result => {
+			this.result = result;
+			this.onload();
+		};
+
+		this._mockError = error => {
+			this.error = error;
+			this.onerror();
+		};
+
+		this._mockAbort = () => {
+			this.onabort();
+		};
+
+		this._mockProgress = progress => {
+			this.onprogress( { loaded: progress } );
+		};
+	}
+} );