fix(uploader): add error info and fix issues in uploader
This commit is contained in:
parent
e462c349a5
commit
3b1508af51
6 changed files with 138 additions and 92 deletions
|
@ -8,7 +8,7 @@ server:
|
|||
debug: true
|
||||
host: "127.0.0.1"
|
||||
port: 8686
|
||||
readTimeout: 2000
|
||||
readTimeout: 4000
|
||||
writerTimeout: 86400000 # 1 day
|
||||
maxHeaderBytes: 512
|
||||
publicPath: "public"
|
||||
|
|
|
@ -725,3 +725,9 @@ div.hr {
|
|||
overflow-wrap: break-word;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.alert-red {
|
||||
border: dashed 1px #e74c3c;
|
||||
color: #e74c3c;
|
||||
border-radius: 0.5rem;
|
||||
}
|
|
@ -33,7 +33,7 @@ export interface BrowserProps {
|
|||
dirPath: List<string>;
|
||||
isSharing: boolean;
|
||||
items: List<MetadataResp>;
|
||||
uploadings: List<UploadInfo>;
|
||||
uploadings: List<UploadEntry>;
|
||||
sharings: List<string>;
|
||||
|
||||
uploadFiles: List<File>;
|
||||
|
@ -328,8 +328,7 @@ export class Browser extends React.Component<Props, State, {}> {
|
|||
}
|
||||
);
|
||||
|
||||
const nameCellClass = `item-name item-name-${
|
||||
this.props.browser.isVertical ? "vertical" : "horizontal"
|
||||
const nameCellClass = `item-name item-name-${this.props.browser.isVertical ? "vertical" : "horizontal"
|
||||
} pointer`;
|
||||
const sizeCellClass = this.props.browser.isVertical
|
||||
? `hidden margin-s`
|
||||
|
@ -422,8 +421,7 @@ export class Browser extends React.Component<Props, State, {}> {
|
|||
<span className="padding-m">
|
||||
<button
|
||||
onClick={() => this.select(item.name)}
|
||||
className={`${
|
||||
isSelected ? "blue0-bg white-font" : "grey2-bg grey3-font"
|
||||
className={`${isSelected ? "blue0-bg white-font" : "grey2-bg grey3-font"
|
||||
}`}
|
||||
style={{ width: "8rem", display: "inline-block" }}
|
||||
>
|
||||
|
@ -470,8 +468,7 @@ export class Browser extends React.Component<Props, State, {}> {
|
|||
<button
|
||||
type="button"
|
||||
onClick={() => this.select(item.name)}
|
||||
className={`${
|
||||
isSelected ? "blue0-bg white-font" : "grey2-bg grey3-font"
|
||||
className={`${isSelected ? "blue0-bg white-font" : "grey2-bg grey3-font"
|
||||
}`}
|
||||
style={{ width: "8rem", display: "inline-block" }}
|
||||
>
|
||||
|
@ -593,13 +590,13 @@ export class Browser extends React.Component<Props, State, {}> {
|
|||
) : null;
|
||||
|
||||
const uploadingList = this.props.browser.uploadings.map(
|
||||
(uploading: UploadInfo) => {
|
||||
const pathParts = uploading.realFilePath.split("/");
|
||||
(uploading: UploadEntry) => {
|
||||
const pathParts = uploading.filePath.split("/");
|
||||
const fileName = pathParts[pathParts.length - 1];
|
||||
|
||||
return (
|
||||
<div key={uploading.filePath}>
|
||||
<Flexbox
|
||||
key={uploading.realFilePath}
|
||||
children={List([
|
||||
<span className="padding-m">
|
||||
<Flexbox
|
||||
|
@ -622,13 +619,13 @@ export class Browser extends React.Component<Props, State, {}> {
|
|||
|
||||
<div className="item-op padding-m">
|
||||
<button
|
||||
onClick={() => this.stopUploading(uploading.realFilePath)}
|
||||
onClick={() => this.stopUploading(uploading.filePath)}
|
||||
className="grey3-bg grey4-font margin-r-m"
|
||||
>
|
||||
{this.props.msg.pkg.get("browser.stop")}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => this.deleteUpload(uploading.realFilePath)}
|
||||
onClick={() => this.deleteUpload(uploading.filePath)}
|
||||
className="grey3-bg grey4-font"
|
||||
>
|
||||
{this.props.msg.pkg.get("browser.delete")}
|
||||
|
@ -637,6 +634,14 @@ export class Browser extends React.Component<Props, State, {}> {
|
|||
])}
|
||||
childrenStyles={List([{}, { justifyContent: "flex-end" }])}
|
||||
/>
|
||||
{uploading.err.trim() === "" ? null : (
|
||||
<div className="alert-red margin-s">
|
||||
<span className="padding-m">
|
||||
{uploading.err.trim()}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
@ -668,7 +673,7 @@ export class Browser extends React.Component<Props, State, {}> {
|
|||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div className="container">
|
||||
<div className="container padding-b-m">
|
||||
<Flexbox
|
||||
children={List([
|
||||
<span className="padding-m">
|
||||
|
@ -721,8 +726,7 @@ export class Browser extends React.Component<Props, State, {}> {
|
|||
type="text"
|
||||
readOnly
|
||||
className="margin-r-m"
|
||||
value={`${
|
||||
document.location.href.split("?")[0]
|
||||
value={`${document.location.href.split("?")[0]
|
||||
}?dir=${encodeURIComponent(dirPath)}`}
|
||||
/>
|
||||
<button
|
||||
|
|
|
@ -2,6 +2,7 @@ import { List, Set, Map } from "immutable";
|
|||
|
||||
import BgWorker from "../worker/upload.bg.worker";
|
||||
import { FgWorker } from "../worker/upload.fg.worker";
|
||||
import { UploadEntry } from "../worker/interface";
|
||||
|
||||
import { BrowserProps } from "./browser";
|
||||
import { PanesProps } from "./panes";
|
||||
|
@ -9,8 +10,7 @@ import { LoginProps } from "./pane_login";
|
|||
import { AdminProps } from "./pane_admin";
|
||||
|
||||
import { MsgPackage } from "../i18n/msger";
|
||||
import { Item } from "./browser";
|
||||
import { UploadInfo, User, MetadataResp } from "../client";
|
||||
import { User, MetadataResp } from "../client";
|
||||
import { initUploadMgr, IWorker } from "../worker/upload_mgr";
|
||||
|
||||
export interface MsgProps {
|
||||
|
@ -56,7 +56,7 @@ export function initState(): ICoreState {
|
|||
items: List<MetadataResp>([]),
|
||||
sharings: List<string>([]),
|
||||
isSharing: false,
|
||||
uploadings: List<UploadInfo>([]),
|
||||
uploadings: List<UploadEntry>([]),
|
||||
uploadValue: "",
|
||||
uploadFiles: List<File>([]),
|
||||
tab: "",
|
||||
|
|
|
@ -39,7 +39,7 @@ export class Updater {
|
|||
|
||||
initUploads = () => {
|
||||
this.props.browser.uploadings.forEach((entry) => {
|
||||
Up().addStopped(entry.realFilePath, entry.uploaded, entry.size);
|
||||
Up().addStopped(entry.filePath, entry.uploaded, entry.size);
|
||||
});
|
||||
// this.setUploadings(Up().list());
|
||||
};
|
||||
|
@ -63,13 +63,9 @@ export class Updater {
|
|||
};
|
||||
|
||||
setUploadings = (infos: Map<string, UploadEntry>) => {
|
||||
this.props.browser.uploadings = List<UploadInfo>(
|
||||
infos.valueSeq().map((v: UploadEntry): UploadInfo => {
|
||||
return {
|
||||
realFilePath: v.filePath,
|
||||
size: v.size,
|
||||
uploaded: v.uploaded,
|
||||
};
|
||||
this.props.browser.uploadings = List<UploadEntry>(
|
||||
infos.valueSeq().map((entry: UploadEntry): UploadEntry => {
|
||||
return entry;
|
||||
})
|
||||
);
|
||||
};
|
||||
|
@ -105,13 +101,40 @@ export class Updater {
|
|||
};
|
||||
|
||||
refreshUploadings = async (): Promise<boolean> => {
|
||||
// this function get information from server and merge them with local information
|
||||
// because some information (error) can only be detected from local
|
||||
const luResp = await this.filesClient.listUploadings();
|
||||
if (luResp.status !== 200) {
|
||||
// TODO: i18n
|
||||
console.error(luResp.data);
|
||||
return false;
|
||||
}
|
||||
|
||||
this.props.browser.uploadings =
|
||||
luResp.status === 200
|
||||
? List<UploadInfo>(luResp.data.uploadInfos)
|
||||
: this.props.browser.uploadings;
|
||||
return luResp.status === 200;
|
||||
let remoteUploads = Map<string, UploadInfo>([]);
|
||||
luResp.data.uploadInfos.forEach((info: UploadInfo) => {
|
||||
remoteUploads = remoteUploads.set(info.realFilePath, info);
|
||||
})
|
||||
|
||||
let updatedUploads = List<UploadEntry>([]);
|
||||
this.props.browser.uploadings.forEach((localInfo: UploadEntry) => {
|
||||
const remoteInfo = remoteUploads.get(localInfo.filePath);
|
||||
if (remoteInfo == null) {
|
||||
// TODO: i18n
|
||||
localInfo.err = "not found in server";
|
||||
} else {
|
||||
updatedUploads.push({
|
||||
file: localInfo.file,
|
||||
filePath: localInfo.filePath,
|
||||
size: remoteInfo.size,
|
||||
uploaded: remoteInfo.uploaded,
|
||||
state: localInfo.state,
|
||||
err: localInfo.err,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
this.props.browser.uploadings = updatedUploads;
|
||||
return true;
|
||||
};
|
||||
|
||||
stopUploading = (filePath: string) => {
|
||||
|
|
|
@ -6,7 +6,8 @@ import { UploadStatus, UploadState } from "./interface";
|
|||
// TODO: move chunk copying to worker
|
||||
const defaultChunkLen = 1024 * 1024 * 1;
|
||||
const speedDownRatio = 0.5;
|
||||
const speedUpRatio = 1.05;
|
||||
const speedUpRatio = 1.1;
|
||||
const speedLimit = 1024 * 1024 * 10; // 10MB
|
||||
const createRetryLimit = 2;
|
||||
const uploadRetryLimit = 1024;
|
||||
const backoffMax = 2000;
|
||||
|
@ -17,7 +18,6 @@ export interface ReaderResult {
|
|||
}
|
||||
|
||||
export class ChunkUploader {
|
||||
private reader = new FileReader();
|
||||
private client: IFilesClient = new FilesClient("");
|
||||
|
||||
private chunkLen: number = defaultChunkLen;
|
||||
|
@ -70,6 +70,8 @@ export class ChunkUploader {
|
|||
): Promise<UploadStatus> => {
|
||||
if (this.chunkLen === 0) {
|
||||
this.chunkLen = 1; // reset it to 1B
|
||||
} else if (this.chunkLen > speedLimit) {
|
||||
this.chunkLen = speedLimit;
|
||||
} else if (uploaded > file.size) {
|
||||
return {
|
||||
filePath,
|
||||
|
@ -79,13 +81,14 @@ export class ChunkUploader {
|
|||
};
|
||||
}
|
||||
|
||||
const reader = new FileReader();
|
||||
const readerPromise = new Promise<ReaderResult>(
|
||||
(resolve: (result: ReaderResult) => void) => {
|
||||
this.reader.onerror = (_: ProgressEvent<FileReader>) => {
|
||||
resolve({ err: this.reader.error });
|
||||
reader.onerror = (_: ProgressEvent<FileReader>) => {
|
||||
resolve({ err: reader.error });
|
||||
};
|
||||
|
||||
this.reader.onloadend = (ev: ProgressEvent<FileReader>) => {
|
||||
reader.onloadend = (ev: ProgressEvent<FileReader>) => {
|
||||
const dataURL = ev.target.result as string; // readAsDataURL
|
||||
const base64Chunk = dataURL.slice(dataURL.indexOf(",") + 1);
|
||||
resolve({ chunk: base64Chunk });
|
||||
|
@ -93,12 +96,22 @@ export class ChunkUploader {
|
|||
}
|
||||
);
|
||||
|
||||
for (let i = 0; i < 3; i++) {
|
||||
try {
|
||||
const chunkRightPos =
|
||||
uploaded + this.chunkLen > file.size
|
||||
? file.size
|
||||
: uploaded + this.chunkLen;
|
||||
const blob = file.slice(uploaded, chunkRightPos);
|
||||
this.reader.readAsDataURL(blob);
|
||||
reader.readAsDataURL(blob);
|
||||
break;
|
||||
} catch (e) {
|
||||
// The object is already busy reading Blobs
|
||||
console.error(e);
|
||||
await this.backOff();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
const result = await readerPromise;
|
||||
if (result.err != null) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue