parent
30c963a5f0
commit
61a1c93f0f
89 changed files with 15859 additions and 2 deletions
202
client/libs/api_upload.js
Normal file
202
client/libs/api_upload.js
Normal file
|
@ -0,0 +1,202 @@
|
|||
import axios from "axios";
|
||||
import { config } from "../config";
|
||||
import { makePostBody } from "./utils";
|
||||
|
||||
const wait = 5000; // TODO: should tune according to backend
|
||||
const retryMax = 100000;
|
||||
const maxUploadLen = 20 * 1024 * 1024;
|
||||
|
||||
// TODO: add to react-intl
|
||||
const msgUploadFailed = "Fail to upload, upload is stopped.";
|
||||
const msgUploadFailedAndRetry = "Fail to upload, retrying...";
|
||||
const msgFileExists = "File exists.";
|
||||
const msgTooBigChunk = "Too big chunk.";
|
||||
const msgFileNotFound = "File not found, upload stopped.";
|
||||
|
||||
function randomWait() {
|
||||
return Math.random() * wait;
|
||||
}
|
||||
|
||||
function isKnownErr(res) {
|
||||
return res != null && res.Code != null && res.Msg != null;
|
||||
}
|
||||
|
||||
export class FileUploader {
|
||||
constructor(onStart, onProgress, onFinish, onError) {
|
||||
this.onStart = onStart;
|
||||
this.onProgress = onProgress;
|
||||
this.onFinish = onFinish;
|
||||
this.onError = onError;
|
||||
this.retry = retryMax;
|
||||
this.reader = new FileReader();
|
||||
|
||||
this.uploadFile = file => {
|
||||
return this.startUpload(file);
|
||||
};
|
||||
|
||||
this.startUpload = file => {
|
||||
return axios
|
||||
.post(
|
||||
`${config.serverAddr}/startupload`,
|
||||
makePostBody({
|
||||
fname: file.name
|
||||
})
|
||||
)
|
||||
.then(response => {
|
||||
if (
|
||||
response.data.ShareId == null ||
|
||||
response.data.Start === null ||
|
||||
response.data.Length === null
|
||||
) {
|
||||
throw response;
|
||||
} else {
|
||||
this.onStart(response.data.ShareId, file.name);
|
||||
return this.upload(
|
||||
{
|
||||
shareId: response.data.ShareId,
|
||||
start: response.data.Start,
|
||||
length: response.data.Length
|
||||
},
|
||||
file
|
||||
);
|
||||
}
|
||||
})
|
||||
.catch(response => {
|
||||
// TODO: this is not good because error may not be response
|
||||
if (isKnownErr(response.data) && response.data.Code === 429) {
|
||||
setTimeout(this.startUpload, randomWait(), file);
|
||||
} else if (isKnownErr(response.data) && response.data.Code === 412) {
|
||||
this.onError(msgFileExists);
|
||||
} else if (isKnownErr(response.data) && response.data.Code === 404) {
|
||||
this.onError(msgFileNotFound);
|
||||
} else if (this.retry > 0) {
|
||||
this.retry--;
|
||||
this.onError(msgUploadFailedAndRetry);
|
||||
console.trace(response);
|
||||
setTimeout(this.startUpload, randomWait(), file);
|
||||
} else {
|
||||
this.onError(msgUploadFailed);
|
||||
console.trace(response);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
this.prepareReader = (shareInfo, end, resolve, reject) => {
|
||||
this.reader.onerror = err => {
|
||||
reject(err);
|
||||
};
|
||||
|
||||
this.reader.onloadend = event => {
|
||||
const formData = new FormData();
|
||||
formData.append("shareid", shareInfo.shareId);
|
||||
formData.append("start", shareInfo.start);
|
||||
formData.append("len", end - shareInfo.start);
|
||||
formData.append("chunk", new Blob([event.target.result]));
|
||||
|
||||
const url = `${config.serverAddr}/upload`;
|
||||
const headers = {
|
||||
"Content-Type": "multipart/form-data"
|
||||
};
|
||||
|
||||
try {
|
||||
axios
|
||||
.post(url, formData, { headers })
|
||||
.then(response => resolve(response))
|
||||
.catch(err => {
|
||||
reject(err);
|
||||
});
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
this.upload = (shareInfo, file) => {
|
||||
const uploaded = shareInfo.start + shareInfo.length;
|
||||
const end = uploaded < file.size ? uploaded : file.size;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
if (
|
||||
end == null ||
|
||||
shareInfo.start == null ||
|
||||
end - shareInfo.start >= maxUploadLen
|
||||
) {
|
||||
throw new Error(msgTooBigChunk);
|
||||
}
|
||||
|
||||
const chunk = file.slice(shareInfo.start, end);
|
||||
this.prepareReader(shareInfo, end, resolve, reject);
|
||||
this.reader.readAsArrayBuffer(chunk);
|
||||
})
|
||||
.then(response => {
|
||||
if (
|
||||
response.data.ShareId == null ||
|
||||
response.data.Start == null ||
|
||||
response.data.Length == null ||
|
||||
response.data.Start !== end
|
||||
) {
|
||||
throw response;
|
||||
} else {
|
||||
if (end < file.size) {
|
||||
this.onProgress(shareInfo.shareId, end / file.size);
|
||||
return this.upload(
|
||||
{
|
||||
shareId: shareInfo.shareId,
|
||||
start: shareInfo.start + shareInfo.length,
|
||||
length: shareInfo.length
|
||||
},
|
||||
file
|
||||
);
|
||||
} else {
|
||||
return this.finishUpload(shareInfo);
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(response => {
|
||||
// possible error: response.data.Start == null || response.data.Start !== end
|
||||
if (isKnownErr(response.data) && response.data.Code === 429) {
|
||||
setTimeout(this.upload, randomWait(), shareInfo, file);
|
||||
} else if (isKnownErr(response.data) && response.data.Code === 404) {
|
||||
this.onError(msgFileNotFound);
|
||||
} else if (this.retry > 0) {
|
||||
this.retry--;
|
||||
setTimeout(this.upload, randomWait(), shareInfo, file);
|
||||
this.onError(msgUploadFailedAndRetry);
|
||||
console.trace(response);
|
||||
} else {
|
||||
this.onError(msgUploadFailed);
|
||||
console.trace(response);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
this.finishUpload = shareInfo => {
|
||||
return axios
|
||||
.post(`${config.serverAddr}/finishupload?shareid=${shareInfo.shareId}`)
|
||||
.then(response => {
|
||||
// TODO: should check Code instead of Url
|
||||
if (response.data.ShareId != null && response.data.Start == null) {
|
||||
this.onFinish();
|
||||
return response.data.ShareId;
|
||||
} else {
|
||||
throw response;
|
||||
}
|
||||
})
|
||||
.catch(response => {
|
||||
if (isKnownErr(response.data) && response.data.Code === 429) {
|
||||
setTimeout(this.finishUpload, randomWait(), shareInfo);
|
||||
} else if (isKnownErr(response.data) && response.data.Code === 404) {
|
||||
this.onError(msgFileNotFound);
|
||||
} else if (this.retry > 0) {
|
||||
this.retry--;
|
||||
setTimeout(this.finishUpload, randomWait(), shareInfo);
|
||||
this.onError(msgUploadFailedAndRetry);
|
||||
console.trace(response);
|
||||
} else {
|
||||
this.onError(msgUploadFailed);
|
||||
console.trace(response);
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue