事先說明:upload 所使用"rc-upload"組件在npm是有單獨的包,upload對其進一步封裝吗讶。"rc-upload"
有更多的API選擇。
需求
要求限制上傳圖片的格式恋捆、大小照皆、分辨率。
簡單介紹
這是一個最簡單的upload組件使用
<Upload action="...">
上傳
</Upload>
簡單說一下關(guān)鍵幾個參數(shù)
參數(shù) | 作用 |
---|---|
action | 上傳的服務(wù)器地址沸停,使用默認(rèn)上傳行為必填 |
beforeUpload | 默認(rèn)上傳行為之前的鉤子函數(shù)膜毁,用來限制上傳文件 |
customRequest | 自定義上傳(本文關(guān)鍵) |
fileList | 已上傳列表 |
本文的關(guān)鍵就在于customRequest、fileList和onRemove三個api星立。
平常使用
該組件已經(jīng)將上傳文件封裝的及其簡單
<Upload action="..." onChange={...} beforeUpload={...}>
上傳
</Upload>
在所有提供的鉤子函數(shù)都會接受file參數(shù)爽茴。它就是用戶上傳文件的RcFile形式葬凳。
主要參數(shù)有
{
type // 文件格式
size // 文件大小
status // 狀態(tài)有:uploading done error removed 只有在onChange事件才會變化
response // 服務(wù)端響應(yīng)內(nèi)容绰垂,
}
在beforeUpload調(diào)用file的type和size來限制上傳文件。beforeUpload如果返回false就是取消上傳行為火焰。
來至官網(wǎng)的示例
function beforeUpload(file) {
const isJPG = file.type === 'image/jpeg';
if (!isJPG) {
message.error('You can only upload JPG file!');
}
const isLt2M = file.size / 1024 / 1024 < 2;
if (!isLt2M) {
message.error('Image must smaller than 2MB!');
}
return isJPG && isLt2M;
}
當(dāng)文件開始上傳的時候劲装,這時候調(diào)用onChange 讀取到file.response獲取到服務(wù)端的回調(diào),來實現(xiàn)我們的功能。
進一步使用
- 顯然默認(rèn)的行為不能實現(xiàn)我的要求,file對象并沒有分辨率的參數(shù)占业。我所采用的是把上傳的圖片實例化讀取它的高寬绒怨。這樣出現(xiàn)一個問題。image只有在觸發(fā)load事件之后才能被讀取寬高谦疾,我們沒有辦法將我們的判斷傳遞給beforeUpload南蹂,也就阻止不了上傳事件。
const { file } = files
// files是customRequest參數(shù)
const reader = new FileReader();
reader.addEventListener("load", e => {
const data = e.target.result;
//加載圖片獲取圖片真實寬度和高度
const image = new Image();
image.addEventListener("load", () => {
const w = image.width;
const h = image.height;
});
image.src = data;
});
reader.readAsDataURL(file);
// 我們傳遞不出false念恍。
所有只能使用customRequest來覆蓋默認(rèn)上傳六剥。但這樣有兩個弊端。
上傳狀態(tài)無法被onChange捕獲峰伙。
我們需要自己控制fileList疗疟。
組件showUploadList會出現(xiàn)我們不想展示的圖片。
其實到這一步已經(jīng)可以實現(xiàn)效果瞳氓,但是我想要組件的showUploadList所展示的上傳列表策彤,畢竟別人已經(jīng)寫好動畫了。
所有我們要控制fileList于state綁定匣摘,初始值設(shè)為[]店诗,上傳成功后fileList增加新的元素。
customRequest = (files) => {
const { file } = files
...// 前面限制
let formData = new FormData();
formData.append("file", file);
http('',formData).then(
res =>
this.setState(() => ({ fileList: [{ ...file }, { url, status: "done" }] }));
)
}
設(shè)置的fileList是安裝官方defaultFileList 的形式添加的
{
uid: '1',
name: 'xxx.png',
status: 'done',
response: 'Server Error 500', // custom error message to show
url: 'http://www.baidu.com/xxx.png',
}
onRemove
使用了showUploadList就需要使用onRemove來刪除文件列表元素音榜。我們先看看onRemove的介紹
點擊移除文件時的回調(diào)必搞,返回值為 false 時不移除。支持返回一個 Promise 對象囊咏,Promise 對象 resolve(false) 或 reject 時不移除
由于同時涉及到表單我也用了Form組件恕洲,同樣也要使用Form組件的表單驗證。
onRemove = () => {
this.setState(() => ({
fileList: []
}));
}
圖片是刪除里但是并沒有觸發(fā)Form的驗證梅割。Form都是靠表單的onChange來觸發(fā)的霜第。所有查了一下源碼。
handleRemove(file: UploadFile) {
const { onRemove } = this.props;
Promise.resolve(typeof onRemove === 'function' ? onRemove(file) : onRemove).then(ret => {
// Prevent removing file
if (ret === false) {
return;
}
const removedFileList = removeFileItem(file, this.state.fileList);
if (removedFileList) {
this.onChange({
file,
fileList: removedFileList,
});
}
});
}
其中的 removeFileItem 如下
export function removeFileItem(file: UploadFile, fileList: UploadFile[]) {
const matchKey = file.uid !== undefined ? 'uid' : 'name';
const removed = fileList.filter(item => item[matchKey] !== file[matchKey]);
if (removed.length === fileList.length) {
return null;
}
return removed;
}
可以看出之前fileList已被我們設(shè)為[]户辞,removeFileItem 返回為null泌类,所以沒有觸發(fā)onChange。沒辦法底燎,我們只能自己觸發(fā)了,傳入?yún)?shù)上面的代碼已給出刃榨。
<Upload ref = ref => this.ref = ref>
上傳
</Upload>
onRemove = () => {
...// 前面操作
this.ref.onChange({file,[]})
}
至此我個人所有需求全部解決。但是我在逛github的Issues 的時候發(fā)現(xiàn)有人提這樣無法獲取上傳的進度双仍。
ajax是有原生獲取上傳文件進度的方法的枢希。我使用的是axios的onUploadProgress方法。
onUploadProgress:(progressEvent) => {
const {lengthComputable, loaded, total} = progressEvent
lengthComputable, //是否能夠被讀取長度
loaded// 已上傳,
total //以下載
}