Skip to content

Commit

Permalink
Add rate to files and queue
Browse files Browse the repository at this point in the history
  • Loading branch information
RobbieTheWagner committed Nov 13, 2023
1 parent af05180 commit 1475fe9
Show file tree
Hide file tree
Showing 7 changed files with 90 additions and 2 deletions.
1 change: 1 addition & 0 deletions docs/uploading.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ In addition to the file list, the queue tracks properties that indicate the prog
| queue.size | `number` – Total size of all files currently being uploaded in bytes. |
| queue.loaded | `number` – Number of bytes that have been uploaded to the server. |
| queue.progress | `number` – Current progress of all uploads, as a percentage in the range of 0 to 100. |
| queue.rate | `number` – Current time in ms it is taking to upload 1 byte. |

```hbs
{{#if queue.files.length}}
Expand Down
11 changes: 11 additions & 0 deletions ember-file-upload/src/queue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,17 @@ export class Queue {
return [...this.#distinctFiles.values()];
}

/**
* The current time in ms it is taking to upload 1 byte.
*
* @defaultValue 0
*/
get rate(): number {
return this.files.reduce((acc, { rate }) => {
return acc + rate;
}, 0);
}

/**
* The total size of all files currently being uploaded in bytes.
*
Expand Down
11 changes: 11 additions & 0 deletions ember-file-upload/src/services/file-queue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,17 @@ export default class FileQueueService extends Service {
}, []);
}

/**
* The current time in ms it is taking to upload 1 byte.
*
* @defaultValue 0
*/
get rate(): number {
return this.files.reduce((acc, { rate }) => {
return acc + rate;
}, 0);
}

/**
* The total size of all files currently being uploaded in bytes.
*
Expand Down
16 changes: 16 additions & 0 deletions ember-file-upload/src/system/upload.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { assert } from '@ember/debug';
import { next } from '@ember/runloop';
import HTTPRequest from './http-request.ts';
import RSVP from 'rsvp';
import { waitForPromise } from '@ember/test-waiters';
Expand Down Expand Up @@ -65,6 +66,8 @@ export function onloadstart(
// The correct should be returned while progress
file.size = Math.max(file.size, event.total);
file.progress = (file.loaded / file.size) * 100;
file.bytesWhenProgressLastUpdated = event.loaded;
file.timestampWhenProgressLastUpdated = new Date().getTime();
}

export function onprogress(
Expand All @@ -88,6 +91,13 @@ export function onprogress(
}
file.loaded = Math.max(loaded, file.loaded);
file.progress = (file.loaded / file.size) * 100;

const updatedTime = new Date().getTime();

next(() => {
file.bytesWhenProgressLastUpdated = file.loaded;
file.timestampWhenProgressLastUpdated = updatedTime;
});
}

export function onloadend(
Expand All @@ -100,6 +110,8 @@ export function onloadend(
file.loaded = file.size;
file.progress = (file.loaded / file.size) * 100;
file.isUploadComplete = true;
file.bytesWhenProgressLastUpdated = 0;
file.timestampWhenProgressLastUpdated = 0;
}

export function upload(
Expand Down Expand Up @@ -142,10 +154,14 @@ export function upload(
request.onloadend = (event) => onloadend(file, event);

request.ontimeout = () => {
file.bytesWhenProgressLastUpdated = 0;
file.timestampWhenProgressLastUpdated = 0;
file.state = FileState.TimedOut;
file.queue?.flush();
};
request.onabort = () => {
file.bytesWhenProgressLastUpdated = 0;
file.timestampWhenProgressLastUpdated = 0;
file.state = FileState.Aborted;
file.queue?.flush();
};
Expand Down
24 changes: 24 additions & 0 deletions ember-file-upload/src/upload-file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,19 @@ export class UploadFile {
this.#name = value;
}

/** The current speed in ms that it takes to upload one byte */
get rate() {
const updatedTime = new Date().getTime();
const timeElapsedSinceLastUpdate =
updatedTime - this.timestampWhenProgressLastUpdated;

const bytesTransferredSinceLastUpdate =
this.loaded - this.bytesWhenProgressLastUpdated;

// Divide by elapsed time to get bytes per millisecond
return bytesTransferredSinceLastUpdate / timeElapsedSinceLastUpdate;
}

#size = 0;

/** The size of the file in bytes. */
Expand Down Expand Up @@ -79,6 +92,11 @@ export class UploadFile {
return this.type.split('/').slice(-1)[0] ?? '';
}

/**
* Tracks the number of bytes that had been uploaded when progress values last changed.
*/
@tracked bytesWhenProgressLastUpdated = 0;

/** The number of bytes that have been uploaded to the server */
@tracked loaded = 0;

Expand Down Expand Up @@ -138,6 +156,12 @@ export class UploadFile {
// */
// source?: FileSource;

/**
* The timestamp of when the progress last updated in milliseconds. Used to calculate the time
* that has elapsed.
*/
@tracked timestampWhenProgressLastUpdated = 0;

/**
* Upload file with `application/octet-stream` content type.
*
Expand Down
14 changes: 13 additions & 1 deletion test-app/tests/integration/progress-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ import { setupRenderingTest } from 'ember-qunit';
import { render } from '@ember/test-helpers';
import { hbs } from 'ember-cli-htmlbars';
import { selectFiles } from 'ember-file-upload/test-support';
import { type MirageTestContext, setupMirage } from 'ember-cli-mirage/test-support';
import {
type MirageTestContext,
setupMirage,
} from 'ember-cli-mirage/test-support';
import { TrackedArray } from 'tracked-built-ins';
import { type Asset } from 'test-app/components/demo-upload';
import type Owner from '@ember/owner';
Expand Down Expand Up @@ -66,13 +69,16 @@ module('Integration | progress', function (hooks) {
selectFiles('#upload-photo', file, file, file);
}

assert.strictEqual(queue.rate, 0, 'rate should be 0 before uploads begin');

await firstFile.promise;

assert.strictEqual(
queue.progress,
33,
'first file uploaded - queue progress 33%',
);
assert.true(queue.rate > 0, 'rate should be > 0 when uploading');
assert.strictEqual(
queue.files.length,
3,
Expand All @@ -86,6 +92,7 @@ module('Integration | progress', function (hooks) {
66,
'second file uploaded - queue progress 66%',
);
assert.true(queue.rate > 0, 'rate should be > 0 when uploading');
assert.strictEqual(
queue.files.length,
3,
Expand All @@ -99,6 +106,11 @@ module('Integration | progress', function (hooks) {
0,
'third file uploaded - progress is back to 0% since the queue has been flushed',
);
assert.strictEqual(
queue.rate,
0,
'rate should be 0 after all uploads finish',
);
assert.strictEqual(
queue.files.length,
0,
Expand Down
15 changes: 14 additions & 1 deletion test-app/tests/unit/upload-file-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ import { setupTest } from 'ember-qunit';
import { uploadHandler } from 'ember-file-upload';
import { UploadFile, FileSource } from 'ember-file-upload';

import { type MirageTestContext, setupMirage } from 'ember-cli-mirage/test-support';
import {
type MirageTestContext,
setupMirage,
} from 'ember-cli-mirage/test-support';

module('Unit | UploadFile', function (hooks) {
setupTest(hooks);
Expand Down Expand Up @@ -85,4 +88,14 @@ module('Unit | UploadFile', function (hooks) {
assert.strictEqual(file.size, 13);
assert.strictEqual(file.file.size, 9);
});

test('it correctly calculates rate', function (assert) {
const file = UploadFile.fromBlob(new Blob(['test text'], { type: 'text' }));
const twoSecondsAgo = new Date().getTime() - 2000;
file.timestampWhenProgressLastUpdated = twoSecondsAgo;
file.bytesWhenProgressLastUpdated = 5000;
file.loaded = 20000;
file.size = 500000;
assert.strictEqual(Math.ceil(file.rate), 8);
});
});

0 comments on commit 1475fe9

Please sign in to comment.