Skip to content

New: Chunked uploads#15

Merged
tonyjin merged 1 commit intobox:masterfrom
tonyjin:supercharged-uploads
Jul 12, 2017
Merged

New: Chunked uploads#15
tonyjin merged 1 commit intobox:masterfrom
tonyjin:supercharged-uploads

Conversation

@tonyjin
Copy link
Copy Markdown
Contributor

@tonyjin tonyjin commented Jul 11, 2017

  • Files >50MB will be uploaded with the chunked uploads API by default
  • The 'chunked' Content Uploader option can be set to false to disable chunked uploads
  • Chunk upload failures due to rate limiting or network errors will be retried until success

Copy link
Copy Markdown
Contributor

@priyajeet priyajeet left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still 1 file left to review, the big one

Comment thread src/api/Chunk.js Outdated

class Chunk extends Base {
cancelled: boolean;
chunk: Object | null;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you can do ?Object, then | null can be removed

Comment thread src/api/Chunk.js Outdated
data: Object = {};
progress: number = 0;
retry: number;
uploadHeaders: Object;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

type StringMap from flowTypes

Comment thread src/api/Chunk.js Outdated
* @returns {void}
*/
upload(): void {
if (this.cancelled) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If subclassing Base.js, it has the concept of this.isDestroyed(), can reuse that. See other API files.

Comment thread src/api/Chunk.js Outdated
clearTimeout(this.retry);
this.chunk = null;
this.data = {};
this.cancelled = true;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this.destroy() can be used here

Comment thread src/api/Chunk.js

const UPLOAD_RETRY_INTERVAL_MS = 1000;

class Chunk extends Base {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason this needs to be a child of Base?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Auth information + needs its own XHR.

Comment thread src/util/base64.js
* @return {string}
*/
/* eslint-disable no-bitwise */
export default function(numArray: Int32Array): string {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably add a test for this file while you are at it, you can steal it from jsperf

Comment thread src/util/Xhr.js Outdated
delete hdrs.Accept;
delete hdrs['Content-Type'];

if (headers['Content-Type']) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Content-Type string being repeated 4+ times

Comment thread src/util/Xhr.js Outdated
Object.keys(data).forEach((key) => {
formData.append(key, data[key]);
});
postFile({
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably can rename to upload or something, since its PUT/POST both

Comment thread src/api/index.js
this.destroy();
this.uploadAPI = new UploadAPI(this.options);
getUploadAPI(chunked?: boolean, fileSize?: number): ChunkedUploadAPI | PlainUploadAPI {
if (this.uploadAPI) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason why you are returning the prior one?
What if it was destroyed()?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I need to be able to cancel - fetching a different API won't cancel the right one.

Comment thread src/api/ChunkedUpload.js Outdated
this.chunks = [];

// Abort upload session
this.xhr.delete(`${this.uploadHost}/api/2.0/files/upload_sessions/${this.sessionId}`);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe have this as a function somewhere, I think there is another place its used

Comment thread src/api/ChunkedUpload.js
* @returns {void}
*/
cancel(): void {
// Cancel individual upload chunks
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably add destroyedCheck()

Comment thread src/api/ChunkedUpload.js
* @return {Promise}
*/
getNextChunk(): Promise<any> {
return new Promise((resolve, reject) => {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should add destroyedCheck

Comment thread src/api/ChunkedUpload.js
} else {
// Otherwise, reupload and append timestamp
// 'test.jpg' becomes 'test-TIMESTAMP.jpg'
const extension = name.substr(name.lastIndexOf('.')) || '';
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bug when file has no extension

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can also pass in the name from the file object
also see RenameDialog.js

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Known issue - there's no good cross-browser way to distinguish between a file/folder from the File API so for now I'm using the no extension = folder heuristic

Comment thread src/api/ChunkedUpload.js Outdated
// Use provided upload URL if passed in, otherwise construct
let uploadSessionUrl = url;
if (!uploadSessionUrl) {
uploadSessionUrl = `${this.uploadHost}/api/2.0/files/upload_sessions`;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Creating a url can be moved to base.js along with the other api one

Comment thread src/api/ChunkedUpload.js
}

for (let i = 0; i < UPLOAD_PARALLELISM; i += 1) {
this.getNextChunk().then((chunk) => (chunk ? this.uploadChunk(chunk) : this.commitFile())).catch(() => {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should catch just call this.uploadSessionErrorHandler?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, conceptually different - but this is unrecoverable though so I'll call the error callback

Comment thread src/api/ChunkedUpload.js Outdated

// Read the blob as an ArrayBuffer so SHA1 can be calculated with Rusha
const fileReader = new FileReader();
fileReader.addEventListener('load', (event: any) => {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

event: Event & { target: EventTarget }

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this syntax exactly?

Comment thread src/api/ChunkedUpload.js Outdated
resolve(chunk);
});

fileReader.addEventListener('error', () => {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fileReader.addEventListener('error', reject) ?

Comment thread src/api/ChunkedUpload.js Outdated
*
* @return {Promise}
*/
getNextChunk(): Promise<any> {
Copy link
Copy Markdown
Contributor

@priyajeet priyajeet Jul 11, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Promise<Chunk> works?

- Files >50MB will be uploaded with the chunked uploads API by default
- The 'chunked' Content Uploader option can be set to false to disable chunked uploads
- Chunk upload failures due to rate limiting or network errors will be retried until success
@tonyjin tonyjin merged commit 59feabc into box:master Jul 12, 2017
@tonyjin tonyjin deleted the supercharged-uploads branch July 12, 2017 00:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants