fix(uploader): add error info and fix issues in uploader

This commit is contained in:
hexxa 2021-09-12 20:35:34 +08:00 committed by Hexxa
parent e462c349a5
commit 3b1508af51
6 changed files with 138 additions and 92 deletions

View file

@ -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"

View file

@ -725,3 +725,9 @@ div.hr {
overflow-wrap: break-word;
display: block;
}
.alert-red {
border: dashed 1px #e74c3c;
color: #e74c3c;
border-radius: 0.5rem;
}

View file

@ -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

View file

@ -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: "",

View file

@ -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) => {

View file

@ -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,12 +18,11 @@ export interface ReaderResult {
}
export class ChunkUploader {
private reader = new FileReader();
private client: IFilesClient = new FilesClient("");
private chunkLen: number = defaultChunkLen;
constructor() {}
constructor() { }
setClient = (client: IFilesClient) => {
this.client = client;
@ -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) {