fix: 图片&文件控件文件上传过程中取消逻辑完善 (#2092)

* fix: 修复图片取消上传导致的验证死锁问题

* fix: File 上传过程中删除问题细节优化
This commit is contained in:
liaoxuezhi 2021-06-15 14:41:08 +08:00 committed by GitHub
parent 7f7e3b4fc1
commit 58c4530a88
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 91 additions and 12 deletions

View File

@ -0,0 +1,8 @@
{
"status": 0,
"msg": "ok",
"data": {
"uploadId": "xjfkdajkfekjfdkajfkdjs",
"key": "file-233"
}
}

View File

@ -301,6 +301,10 @@ export default class FileControl extends React.Component<FileProps, FileState> {
current: FileValue | FileX | null;
resolve?: (value?: any) => void;
emitValue: any;
fileUploadCancelExecutors: Array<{
file: any;
executor: () => void;
}> = [];
static valueToFile(
value: string | FileValue,
@ -787,13 +791,19 @@ export default class FileControl extends React.Component<FileProps, FileState> {
removeFile(file: FileX | FileValue, index: number) {
const files = this.state.files.concat();
this.removeFileCanelExecutor(file, true);
files.splice(index, 1);
const isUploading = this.current === file;
if (isUploading) {
this.current = null;
}
this.setState(
{
files: files
},
this.onChange
isUploading ? this.tick : this.onChange
);
}
@ -882,7 +892,9 @@ export default class FileControl extends React.Component<FileProps, FileState> {
// Note: File类型字段放在后面可以支持第三方云存储鉴权
fd.append(config.fieldName || 'file', file);
return this._send(api, fd, {}, onProgress);
return this._send(file, api, fd, {}, onProgress).finally(() => {
this.removeFileCanelExecutor(file);
});
}
uploadBigFile(
@ -932,7 +944,7 @@ export default class FileControl extends React.Component<FileProps, FileState> {
}
);
self._send(startApi).then(startChunk).catch(reject);
self._send(file, startApi).then(startChunk).catch(reject);
function startChunk(ret: Payload) {
onProgress(startProgress);
@ -995,7 +1007,13 @@ export default class FileControl extends React.Component<FileProps, FileState> {
}
);
self._send(endApi).then(resolve).catch(reject);
self
._send(file, endApi)
.finally(() => {
self.removeFileCanelExecutor(file);
})
.then(resolve)
.catch(reject);
}
function uploadPartFile(state: ObjectState, conf: Partial<FileProps>) {
@ -1027,7 +1045,7 @@ export default class FileControl extends React.Component<FileProps, FileState> {
fd.append(config.fieldName || 'file', blob, file.name);
return self
._send(api, fd, {}, progress =>
._send(file, api, fd, {}, progress =>
updateProgress(task.partNumber, progress)
)
.then(ret => {
@ -1069,6 +1087,7 @@ export default class FileControl extends React.Component<FileProps, FileState> {
}
_send(
file: FileX,
api: ApiObject | ApiString,
data?: any,
options?: object,
@ -1084,6 +1103,13 @@ export default class FileControl extends React.Component<FileProps, FileState> {
method: 'post',
...options,
withCredentials: true,
cancelExecutor: (cancelExecutor: () => void) => {
// 记录取消器,取消的时候要调用
this.fileUploadCancelExecutors.push({
file: file,
executor: cancelExecutor
});
},
onUploadProgress: onProgress
? (event: {loaded: number; total: number}) =>
onProgress(event.loaded / event.total)
@ -1091,6 +1117,18 @@ export default class FileControl extends React.Component<FileProps, FileState> {
});
}
removeFileCanelExecutor(file: any, execute = false) {
this.fileUploadCancelExecutors = this.fileUploadCancelExecutors.filter(
item => {
if (execute && item.file === file) {
item.executor();
}
return item.file !== file;
}
);
}
validate(): any {
const __ = this.props.translate;
@ -1239,7 +1277,7 @@ export default class FileControl extends React.Component<FileProps, FileState> {
</span>
</>
) : null}
{file.state !== 'uploading' && !disabled ? (
{!disabled ? (
<a
data-tooltip={__('Select.clear')}
className={cx('FileControl-clear')}

View File

@ -361,6 +361,10 @@ export default class ImageControl extends React.Component<
};
files: Array<FileValue | FileX> = [];
fileUploadCancelExecutors: Array<{
file: any;
executor: () => void;
}> = [];
cropper = React.createRef<Cropper>();
dropzone = React.createRef<any>();
frameImageRef = React.createRef<any>();
@ -682,13 +686,19 @@ export default class ImageControl extends React.Component<
removeFile(file: FileValue, index: number) {
const files = this.files.concat();
this.removeFileCanelExecutor(file, true);
files.splice(index, 1);
const isUploading = this.current === file;
if (isUploading) {
this.current = null;
}
this.setState(
{
files: (this.files = files)
},
this.onChange
isUploading ? this.tick : this.onChange
);
}
@ -1048,11 +1058,34 @@ export default class ImageControl extends React.Component<
throw new Error('fetcher is required');
}
return env.fetcher(api, fd, {
method: 'post',
onUploadProgress: (event: {loaded: number; total: number}) =>
onProgress(event.loaded / event.total)
});
return env
.fetcher(api, fd, {
method: 'post',
cancelExecutor: (cancelExecutor: () => void) => {
// 记录取消器,取消的时候要调用
this.fileUploadCancelExecutors.push({
file: file,
executor: cancelExecutor
});
},
onUploadProgress: (event: {loaded: number; total: number}) =>
onProgress(event.loaded / event.total)
})
.finally(() => {
this.removeFileCanelExecutor(file);
});
}
removeFileCanelExecutor(file: any, execute = false) {
this.fileUploadCancelExecutors = this.fileUploadCancelExecutors.filter(
item => {
if (execute && item.file === file) {
item.executor();
}
return item.file !== file;
}
);
}
handleClick() {