75 lines
1.9 KiB
JavaScript
75 lines
1.9 KiB
JavaScript
class Uploader {
|
|
|
|
constructor({file, onProgress}) {
|
|
this.file = file;
|
|
this.onProgress = onProgress;
|
|
|
|
// create fileId that uniquely identifies the file
|
|
// we could also add user session identifier (if had one), to make it even more unique
|
|
this.fileId = file.name + '-' + file.size + '-' + file.lastModified;
|
|
}
|
|
|
|
async getUploadedBytes() {
|
|
let response = await fetch('status', {
|
|
headers: {
|
|
'X-File-Id': this.fileId
|
|
}
|
|
});
|
|
|
|
if (response.status != 200) {
|
|
throw new Error("Can't get uploaded bytes: " + response.statusText);
|
|
}
|
|
|
|
let text = await response.text();
|
|
|
|
return +text;
|
|
}
|
|
|
|
async upload() {
|
|
this.startByte = await this.getUploadedBytes();
|
|
|
|
let xhr = this.xhr = new XMLHttpRequest();
|
|
xhr.open("POST", "upload", true);
|
|
|
|
// send file id, so that the server knows which file to resume
|
|
xhr.setRequestHeader('X-File-Id', this.fileId);
|
|
// send the byte we're resuming from, so the server knows we're resuming
|
|
xhr.setRequestHeader('X-Start-Byte', this.startByte);
|
|
|
|
xhr.upload.onprogress = (e) => {
|
|
this.onProgress(this.startByte + e.loaded, this.startByte + e.total);
|
|
};
|
|
|
|
console.log("send the file, starting from", this.startByte);
|
|
xhr.send(this.file.slice(this.startByte));
|
|
|
|
// return
|
|
// true if upload was successful,
|
|
// false if aborted
|
|
// throw in case of an error
|
|
return await new Promise((resolve, reject) => {
|
|
|
|
xhr.onload = xhr.onerror = () => {
|
|
console.log("upload end status:" + xhr.status + " text:" + xhr.statusText);
|
|
|
|
if (xhr.status == 200) {
|
|
resolve(true);
|
|
} else {
|
|
reject(new Error("Upload failed: " + xhr.statusText));
|
|
}
|
|
};
|
|
|
|
// onabort triggers only when xhr.abort() is called
|
|
xhr.onabort = () => resolve(false);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
stop() {
|
|
if (this.xhr) {
|
|
this.xhr.abort();
|
|
}
|
|
}
|
|
|
|
}
|