Skip to content

Commit 4b6218c

Browse files
committed
Prevent empty last part in progressive upload
1 parent 8a8e5e7 commit 4b6218c

File tree

8 files changed

+78
-21
lines changed

8 files changed

+78
-21
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
# Changelog
22
All changes to this project will be documented in this file.
33

4+
## [1.0.9] - 2022-05-24
5+
- Progressive upload: prevent last part to be empty
6+
47
## [1.0.8] - 2022-04-27
58
- Create a AbstractUploader
69
- Add origin header

README.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -276,10 +276,11 @@ Using delegated upload tokens for authentication is best options when uploading
276276
#### Common options
277277

278278

279-
| Option name | Mandatory | Type | Description |
280-
| ----------: | --------- | ------ | ----------------------------------------------------- |
281-
| apiHost | no | string | api.video host (default: ws.api.video) |
282-
| retries | no | number | number of retries when an API call fails (default: 5) |
279+
| Option name | Mandatory | Type | Description |
280+
| ----------------: | --------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
281+
| apiHost | no | string | api.video host (default: ws.api.video) |
282+
| retries | no | number | number of retries when an API call fails (default: 5) |
283+
| preventEmptyParts | no | boolean | if true, the upload will succeed even if an empty Blob is passed to uploadLastPart(). This may alter performances a bit in some cases (default: false) |
283284

284285

285286
### Example

dist/index.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/src/progressive-video-uploader.d.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
import { AbstractUploader, CommonOptions, VideoUploadResponse, WithAccessToken, WithApiKey, WithUploadToken } from "./abstract-uploader";
2-
export interface ProgressiveUploaderOptionsWithUploadToken extends CommonOptions, WithUploadToken {
2+
export interface ProgressiveUploadCommonOptions {
3+
preventEmptyParts?: boolean;
34
}
4-
export interface ProgressiveUploaderOptionsWithAccessToken extends CommonOptions, WithAccessToken {
5+
export interface ProgressiveUploaderOptionsWithUploadToken extends ProgressiveUploadCommonOptions, CommonOptions, WithUploadToken {
56
}
6-
export interface ProgressiveUploaderOptionsWithApiKey extends CommonOptions, WithApiKey {
7+
export interface ProgressiveUploaderOptionsWithAccessToken extends ProgressiveUploadCommonOptions, CommonOptions, WithAccessToken {
8+
}
9+
export interface ProgressiveUploaderOptionsWithApiKey extends ProgressiveUploadCommonOptions, CommonOptions, WithApiKey {
710
}
811
export interface ProgressiveUploadProgressEvent {
912
uploadedBytes: number;
@@ -18,6 +21,7 @@ export declare class ProgressiveUploader extends AbstractUploader<ProgressivePro
1821
private currentPartBlobs;
1922
private currentPartBlobsSize;
2023
private queue;
24+
private preventEmptyParts;
2125
constructor(options: ProgressiveUploaderOptionsWithAccessToken | ProgressiveUploaderOptionsWithUploadToken | ProgressiveUploaderOptionsWithApiKey);
2226
uploadPart(file: Blob): Promise<void>;
2327
uploadLastPart(file: Blob): Promise<VideoUploadResponse>;

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@api.video/video-uploader",
3-
"version": "1.0.8",
3+
"version": "1.0.9",
44
"description": "api.video video uploader",
55
"repository": {
66
"type": "git",

src/progressive-video-uploader.ts

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
import { AbstractUploader, CommonOptions, MIN_CHUNK_SIZE, VideoUploadResponse, WithAccessToken, WithApiKey, WithUploadToken } from "./abstract-uploader";
22
import { PromiseQueue } from "./promise-queue";
33

4+
export interface ProgressiveUploadCommonOptions {
5+
preventEmptyParts?: boolean;
6+
}
47

5-
export interface ProgressiveUploaderOptionsWithUploadToken extends CommonOptions, WithUploadToken { }
6-
export interface ProgressiveUploaderOptionsWithAccessToken extends CommonOptions, WithAccessToken { }
7-
export interface ProgressiveUploaderOptionsWithApiKey extends CommonOptions, WithApiKey { }
8+
export interface ProgressiveUploaderOptionsWithUploadToken extends ProgressiveUploadCommonOptions, CommonOptions, WithUploadToken { }
9+
export interface ProgressiveUploaderOptionsWithAccessToken extends ProgressiveUploadCommonOptions, CommonOptions, WithAccessToken { }
10+
export interface ProgressiveUploaderOptionsWithApiKey extends ProgressiveUploadCommonOptions, CommonOptions, WithApiKey { }
811

912
export interface ProgressiveUploadProgressEvent {
1013
uploadedBytes: number;
@@ -21,24 +24,40 @@ export class ProgressiveUploader extends AbstractUploader<ProgressiveProgressEve
2124
private currentPartBlobs: Blob[] = [];
2225
private currentPartBlobsSize = 0;
2326
private queue = new PromiseQueue();
27+
private preventEmptyParts: boolean;
2428

2529
constructor(options: ProgressiveUploaderOptionsWithAccessToken | ProgressiveUploaderOptionsWithUploadToken | ProgressiveUploaderOptionsWithApiKey) {
2630
super(options);
31+
this.preventEmptyParts = options.preventEmptyParts || false;
2732
}
2833

2934
public uploadPart(file: Blob): Promise<void> {
3035
this.currentPartBlobsSize += file.size;
3136
this.currentPartBlobs.push(file);
3237

33-
if (this.currentPartBlobsSize >= MIN_CHUNK_SIZE) {
34-
return this.queue.add(() => {
35-
const promise = this.upload(new Blob(this.currentPartBlobs)).then(res => {
36-
this.videoId = res.videoId;
37-
});
38-
this.currentPartNum++;
38+
if ((this.preventEmptyParts && (this.currentPartBlobsSize - file.size >= MIN_CHUNK_SIZE))
39+
|| (!this.preventEmptyParts && (this.currentPartBlobsSize >= MIN_CHUNK_SIZE))) {
40+
41+
let toSend: any[];
42+
if(this.preventEmptyParts) {
43+
toSend = this.currentPartBlobs.slice(0, -1);
44+
this.currentPartBlobs = this.currentPartBlobs.slice(-1);
45+
this.currentPartBlobsSize = this.currentPartBlobs.length === 0 ? 0 : this.currentPartBlobs[0].size;
46+
} else {
47+
toSend = this.currentPartBlobs;
3948
this.currentPartBlobs = [];
4049
this.currentPartBlobsSize = 0;
41-
return promise;
50+
}
51+
52+
return this.queue.add(() => {
53+
if (toSend.length > 0) {
54+
const promise = this.upload(new Blob(toSend)).then(res => {
55+
this.videoId = res.videoId;
56+
});
57+
this.currentPartNum++;
58+
return promise;
59+
}
60+
return new Promise(resolve => resolve());
4261
});
4362
}
4463
return Promise.resolve();

test/progressive-video-uploader.test.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,36 @@ describe('Content-range', () => {
6969
});
7070
});
7171

72+
describe('Prevent empty part', () => {
73+
beforeEach(() => mock.setup());
74+
afterEach(() => mock.teardown());
75+
76+
it('content-range headers are properly set', async () => {
77+
const uploadToken = "the-upload-token";
78+
79+
const uploader = new ProgressiveUploader({uploadToken, preventEmptyParts: true});
80+
81+
const expectedRanges = [
82+
'part 1/*',
83+
'part 2/*',
84+
'part 3/3',
85+
];
86+
87+
mock.post(`https://ws.api.video/upload?token=${uploadToken}`, (req, res) => {
88+
expect(req.header("content-range")).to.be.eq(expectedRanges.shift());
89+
return res.status(201).body("{}");
90+
});
91+
92+
await uploader.uploadPart(new File([new ArrayBuffer(5*1024*1024)], "filename"));
93+
await uploader.uploadPart(new File([new ArrayBuffer(5*1024*1024)], "filename"));
94+
await uploader.uploadPart(new File([new ArrayBuffer(3*1024*1024)], "filename"));
95+
await uploader.uploadLastPart(new Blob());
96+
97+
expect(expectedRanges).has.lengthOf(0);
98+
});
99+
});
100+
101+
72102
describe('Access token auth', () => {
73103
beforeEach(() => mock.setup());
74104
afterEach(() => mock.teardown());

0 commit comments

Comments
 (0)