diff --git a/src/flow.js b/src/flow.js index 3f0c1206..f93e44b5 100644 --- a/src/flow.js +++ b/src/flow.js @@ -1435,7 +1435,7 @@ } else if (s === 'pending') { return 0; } else { - return this.total > 0 ? this.loaded / this.total : 0; + return getSuccessProgress(this.loaded, this.total); } }, @@ -1448,7 +1448,9 @@ var size = this.endByte - this.startByte; // can't return only chunk.loaded value, because it is bigger than chunk size if (this.status() !== 'success') { - size = this.progress() * size; + // Multiply integers before computing progress to prevent floating point issue + // https://github.com/flowjs/flow.js/issues/173 + return getSuccessProgress(this.loaded * size, this.total); } return size; }, @@ -1534,6 +1536,16 @@ setTimeout(fn.bind(context), 0); } + /** + * Get the progress from loaded and total sizes + * @function + * @param {number} loaded Number of uploaded bytes + * @param {number} total Number of total bytes to be uploaded + */ + function getSuccessProgress(loaded, total) { + return total > 0 ? loaded / total : 0; + } + /** * Extends the destination object `dst` by copying all of the properties from * the `src` object(s) to `dst`. You can specify multiple `src` objects. diff --git a/test/uploadSpec.js b/test/uploadSpec.js index 81ef44c9..225b1729 100644 --- a/test/uploadSpec.js +++ b/test/uploadSpec.js @@ -552,6 +552,37 @@ describe('upload file', function() { expect(flow.timeRemaining()).toBe(0); }); + // Related issue: https://github.com/flowjs/flow.js/issues/173 + it('should provide the sizeUploaded() number in integer format', function () { + + flow.opts.testChunks = false; + + // The size 49 was chosen because it's the smallest number causing the division precision bug on my system: + // var foo = 1 / 49; => 0.02040816326530612 + // var bar = 49 * foo; => 0.9999999999999999 + var fileSize = 49; + + var str = ''; + + for (var i = 0; i < fileSize; i++) { + str += 'X'; + } + + flow.addFile(new Blob([str])); + + var fileFirst = flow.files[0]; + + expect(fileFirst.sizeUploaded()).toBe(0); + expect(flow.sizeUploaded()).toBe(0); + + flow.upload(); + + requests[0].progress(1, fileSize, true); + expect(fileFirst.sizeUploaded()).toBe(1); + + flow.cancel(); + }); + it('should allow to hook initFileFn and readFileFn', function () { var error = jasmine.createSpy('error'); var success = jasmine.createSpy('success');