arco-design for React Upload oss 自定义上传
效果如下:
要素拆分
首先我们目前从已知的元素中能看到几个东西
1、loading
2、button
3、文件上传完成的显示
自定义上传
首先,我们需要编写自定义上传模块的代码。目前从接口得知,我们的 阿里云 oss 的上传地址如下:
{
"code": 200,
"message": "Success",
"data": {
"signedUrl": "http://***隐藏***-eu.oss-eu-west-1.aliyuncs.com/2536335147/Snipaste_2022-04-07_09-27-12.png?OSSAccessKeyId=LTAI5tHbTafrbPetMj8m7RxL&Expires=1649299000&Signature=06prxPq96xUmZ44uVauD%2BE0ndK4%3D"
},
"meta": []
}
自定义上传的前提,必须得到后端响应的上传地址。即可 Upload 中的 action
我们可以在 beforeUpload 中进行这一步操作,得到我们的 action
...
const [action, setAction] = useState('/');
...
/**
* 自定义请求
* @param {*} option
* @returns
*/
const customRequest = async option => {
const { onProgress, onError, onSuccess, file } = option;
const xhr = new XMLHttpRequest();
xhr.open('PUT', action);
if (xhr.upload) {
xhr.upload.onprogress = function (event) {
let percent;
if (event.total > 0) {
percent = (event.loaded / event.total) * 100;
}
onProgress(parseInt(percent, 10), event);
};
}
xhr.onerror = function error(e) {
setLoading(false);
onError(e);
};
xhr.onload = function onload() {
if (xhr.status < 200 || xhr.status >= 300) {
return onError(xhr.responseText);
}
onSuccess(xhr.responseText, xhr);
setLoading(false);
setTimeout(() => {
changeFiles();
}, 200);
};
const loadFileBob = file => {
return new Promise(resolve => {
let reader = new FileReader();
reader.readAsArrayBuffer(file);
let blob = null;
reader.onload = e => {
if (typeof e.target.result === 'object') {
blob = new Blob([e.target.result]);
} else {
blob = e.target.result;
}
resolve(blob);
};
});
};
setLoading(true);
const blob = await loadFileBob(file);
xhr.send(blob);
return {
abort() {
xhr.abort();
},
};
};
/**
* 获取配置相关
* @param {} param0
*/
const beforeUpload = async ({ name, size }) => {
const { data, code, message } = await getOssUrl({ object: name });
if (code !== 200) {
Message.error(message);
return;
}
setAction(data.signedUrl);
};
我们开始定义 Upload 组件
<Upload
beforeUpload={beforeUpload}
onAbort={() => {
console.log('cancel');
}}
{...props}
fileList={fileList}
onChange={onChange}
customRequest={customRequest}
>
{props.text ? (
<Button ref={uploadRef} icon={<IconUpload />} type="primary">
{props.text}
</Button>
) : (
<></>
)}
</Upload>
...props
为继承父组件传递的 props
uploadRef
主要是为了 Dom 节点操作
loading
xhr.send
为开始发送操作,所以我们在这一层面,进行 设置 loading 为 true
并且需要 在 onerror
和 上传完成中 设置 loading 为 false
文件上传完成的显示
因为我们目前,主要是向父组件传递 上传成功的文件,一个或者多个。我们可以再 onChange 中进行回调显示
/**
* 变更
*/
const onChange = fileList => {
const _fileList = fileList.map(file => {
if (!file.url) {
file.url = action.split('?')[0];
}
return file;
});
setFileList(_fileList);
};
const changeFiles = () => {
setFileList(fileList => {
let _fileList = fileList.filter(file => {
return file.status === 'done';
});
const limit = props.limit ? props.limit : 1;
if (limit === 1) {
if (_fileList.length === 0) {
props.onChange('');
return;
}
props.onChange(fileList[0]['url']);
return fileList;
}
props.onChange(fileList.map(file => file.url));
setLoading(false);
return fileList;
});
};
完整代码如下
import { Button, Message, Spin, Upload } from '@arco-design/web-react';
import { IconUpload } from '@arco-design/web-react/icon';
import { forwardRef, useEffect, useImperativeHandle, useRef, useState } from 'react';
import { getOssUrl } from '../apis/common';
const UploadComponent = (props, ref) => {
const [action, setAction] = useState('/');
const [fileList, setFileList] = useState(props.defaultFileList ? props.defaultFileList : []);
const [loading, setLoading] = useState(false);
const uploadRef = useRef();
useImperativeHandle(ref, () => ({
click: () => {
console.log(uploadRef.current);
uploadRef.current.click();
},
}));
useEffect(() => {
console.log('props', props);
if (props.loadingchange) {
props.loadingchange(loading);
}
}, [loading]);
/**
* 获取配置相关
* @param {} param0
*/
const beforeUpload = async ({ name, size }) => {
const { data, code, message } = await getOssUrl({ object: name });
if (code !== 200) {
Message.error(message);
return;
}
setAction(data.signedUrl);
};
/**
* 自定义请求
* @param {*} option
* @returns
*/
const customRequest = async option => {
const { onProgress, onError, onSuccess, file } = option;
const xhr = new XMLHttpRequest();
xhr.open('PUT', action);
if (xhr.upload) {
xhr.upload.onprogress = function (event) {
let percent;
if (event.total > 0) {
percent = (event.loaded / event.total) * 100;
}
onProgress(parseInt(percent, 10), event);
};
}
xhr.onerror = function error(e) {
setLoading(false);
onError(e);
};
xhr.onload = function onload() {
if (xhr.status < 200 || xhr.status >= 300) {
return onError(xhr.responseText);
}
onSuccess(xhr.responseText, xhr);
setLoading(false);
setTimeout(() => {
changeFiles();
}, 200);
};
const loadFileBob = file => {
return new Promise(resolve => {
let reader = new FileReader();
reader.readAsArrayBuffer(file);
let blob = null;
reader.onload = e => {
if (typeof e.target.result === 'object') {
blob = new Blob([e.target.result]);
} else {
blob = e.target.result;
}
resolve(blob);
};
});
};
setLoading(true);
const blob = await loadFileBob(file);
xhr.send(blob);
return {
abort() {
xhr.abort();
},
};
};
/**
* 变更
*/
const onChange = fileList => {
const _fileList = fileList.map(file => {
if (!file.url) {
file.url = action.split('?')[0];
}
return file;
});
setFileList(_fileList);
};
const changeFiles = () => {
setFileList(fileList => {
let _fileList = fileList.filter(file => {
return file.status === 'done';
});
const limit = props.limit ? props.limit : 1;
if (limit === 1) {
if (_fileList.length === 0) {
props.onChange('');
return;
}
props.onChange(fileList[0]['url']);
return fileList;
}
props.onChange(fileList.map(file => file.url));
setLoading(false);
return fileList;
});
};
return (
<>
<div style={{ display: `${loading ? 'block' : 'none'}` }}>
<Spin />
</div>
<div style={{ display: `${loading ? 'none' : 'block'}` }}>
<Upload
beforeUpload={beforeUpload}
onAbort={() => {
console.log('cancel');
}}
{...props}
fileList={fileList}
onChange={onChange}
customRequest={customRequest}
>
{props.text ? (
<Button ref={uploadRef} icon={<IconUpload />} type="primary">
{props.text}
</Button>
) : (
<></>
)}
</Upload>
</div>
</>
);
};
export default forwardRef(UploadComponent);
使用方式
<Upload accept="image/*" ref={uploadRef} onChange={uploadAvatar} text={'Upload'} />
<Upload
accept=".mp4"
// tip="Only video, .mp4 can be uploaded, and the size does not exceed 50MB"
showUploadList={true}
limit={1}
text={'上传'}
defaultFileList={
item.video
? [
{
uid: '1',
name: item.video,
url: item.video,
},
]
: []
}
onChange={v => formRef.current.setFieldValue('video', v)}
/>
github 代码
https://github.com/surest-sky/surest-sky.github.io/blob/master/React/ArcoDesignUpload
本文由 邓尘锋 创作,采用 知识共享署名4.0 国际许可协议进行许可
本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名
最后编辑时间为: Nov 29, 2022 at 11:56 am