ember-file-upload
ember-file-upload copied to clipboard
Support for application/octet-stream uploads and file chunking
This looks like a great addon. I like the approach of using a service a lot. For me to use this in my project I would need two things. First, I need to support sending uploads without multipart/form-data encoding. Basically I would just put the blob content in the request body and use application/octet-stream as the content type.
Would you be interested in a PR to add this as an option? Maybe options.encoding with options form (default) or octet?
Second, I'd like to see an option to upload large files using chunking. One way to achieve this would be to depend on something like resumable.js. Is this a direction you are interested in?
Please let me know your thoughts. Thanks!
Yeah, that'd be awesome!
Support for application/octet-stream should be added by #12. Upload in chunks is not supported yet as far as I know. This would either add a lot of additional complexity or require another dependency to be pushed to all consumers. To be honest I'm not sure if this is reasonable for a feature which is only needed for a few special cases. Maybe this is more something for a specialized ember-file-upload-resumable addon? Tending towards closing this one as won't fix.
I implemented a way to upload the files in chunks. It needs some minor tweeks in the upload and normalizeOptions functions. Let me know what you think and if it's worth to open a PR.
export default function uploadChunks(file, url, opts) {
let size = get(file, 'size');
let type = get(file, 'type');
// Chunk
let chunkSize = opts.chunkSize || 2000000; // 2 MB
let chunkStart = opts.chunkStart || 0;
let chunkEnd = Math.min(chunkStart + chunkSize, size);
let chunk = get(file, 'blob').slice(chunkStart, chunkEnd, type);
let filename = get(file, 'name');
let filenameEncoded = encodeURI(filename)
// Headers
opts.headers = {};
opts.headers['Content-Range'] = `bytes ${chunkStart}-${chunkEnd-1}/${size}`;
opts.headers['Content-Disposition'] = `attachment; filename="${filenameEncoded}"`;
// Upload
return upload(file, url, opts, (request, options) => {
// Build the form
let form = new FormData();
Object.keys(options.data).forEach((key) => {
if (key === options.fileKey) {
form.append(key, chunk, filename);
} else {
form.append(key, options.data[key]);
}
});
return request.send(form);
})
.then((response) => {
// Progress
set(file, 'loaded', chunkEnd);
set(file, 'progress', (chunkEnd / size) * 100);
// Next chunk
if ( chunkEnd < size ) {
opts.chunkStart = chunkEnd;
return uploadChunks(file, url, opts)
}
// All chunks finished
set(file, 'state', 'uploaded');
return response;
})
.catch(function(error) {
set(file, 'state', 'failed');
return RSVP.reject(error);
})
.finally(function () {
let queue = get(file, 'queue');
if (queue) {
get(file, 'queue').flush();
}
});
}