技術選型
前端技術選型: React Hook + typescript
antd版本:3.18
使用Upload上傳圖片
上傳效果截圖
預覽效果截圖
項目中完整寫法:
import {Button, Form, Input, Modal, Upload, Icon} from 'antd';
const RegistrationForm = (props: IProps) => {
const {
form: { getFieldDecorator , validateFields},
previewData
} = props;
const [filesList, setFilesList] = useState<UploadFile[]>([]);
const [previewImage, setPreviewImage] = useState('');
const [previewVisible, setPreviewVisible] = useState(false);
const normFile = (e: any) => {
if (Array.isArray(e)) {
return e;
}
return e && e.fileList;
};
// 限制圖片的格式,size,分辨率
const handleBeforeUpload = (file: RcFile, FileList: RcFile[]): boolean | PromiseLike<void> => {
const isJPG = file.type === 'image/jpeg';
const isJPEG = file.type === 'image/jpeg';
const isGIF = file.type === 'image/gif';
const isPNG = file.type === 'image/png';
if (!(isJPG || isJPEG || isGIF || isPNG)) {
Modal.error({
title: '只能上傳JPG 、JPEG 、GIF、 PNG格式的圖片~'
});
return false;
}
const isLt2M = file.size / 1024 / 1024 < 2;
if (!isLt2M) {
Modal.error({
title: '超過2M限制,不允許上傳~'
});
return false;
}
return (isJPG || isJPEG || isGIF || isPNG) && isLt2M && checkImageWH(file);
};
// 返回一個promise:通過檢測則返回reslove;失敗則返回reject,並阻止圖片上傳
const checkImageWH = (file: RcFile): PromiseLike<void> => {
const value: IFileProps = file;
return new Promise((resolve, reject) => {
const filereader = new FileReader();
filereader.onload = (e: any) => {
const src = e.target.result;
const image = new Image();
image.onload = () => {
value.width = image.width;
value.height = image.height;
resolve();
};
image.onerror = reject;
image.src = src;
};
filereader.readAsDataURL(value);
});
};
const handleChange = (info: UploadChangeParam) => {
console.log(info.fileList);
console.log(info.file);
setFilesList(info.fileList);
};
const handleCancel = () => {
setPreviewVisible(false);
};
const handlePreview = (file: UploadFile) => {
const imageUrl = file.url || file.thumbUrl || '';
setPreviewImage(imageUrl);
setPreviewVisible(true);
};
return (
<div>
<Form>
<FormItem label="截圖" {...formItemLayout}>
{getFieldDecorator('upload',
{ valuePropName: 'fileList',
getValueFromEvent: normFile
}
)(
<Upload
action="/api/image/upload/"
data={file => ({
image_file: file
})}
listType="picture-card"
fileList={filesList}
onPreview={handlePreview}
beforeUpload={handleBeforeUpload}
onChange={handleChange}
>
{
filesList.length >= 6 ? null : <Button>
<Icon type="upload"/> Click to upload
</Button>
}
</Upload>
)}
</FormItem>
<Modal visible={previewVisible} footer={null} onCancel={handleCancel}>
<img alt="example" style={{ width: '100%' }} src={previewImage} />
</Modal>
</Form>
</div>);
};
const ProductForms = Form.create<IProps>()(RegistrationForm);
export default ProductForms;
上傳後,點擊圖片預覽,瀏覽器卡死
依據上方的代碼,通過 Antd 的 upload 組件將圖片上傳成功後,點擊圖片的縮略圖,理應可以在當前頁面彈出 Modal,預覽圖片。但實際的結果是,瀏覽器可能會卡死。
定位問題發現,原因是:圖片上傳成功後, upload 會將其轉爲 base64編碼。base64這個字符串太大了,點擊圖片預覽的時候,瀏覽器在解析一大串字符串,然後就卡死了。詳細過程描述如下。
上方代碼中,我們可以把 handleChange(file, fileList)方法中的 file、以及 fileList打印出來看看。 file指的是當前正在上傳的 單個 img,fileList是已上傳的全部 img 列表。 當我上傳完 兩張圖片後, 打印結果如下:
file的打印的結果如下:
{
"uid": "rc-upload-1551084269812-5",
"width": 600,
"height": 354,
"lastModified": 1546701318000,
"lastModifiedDate": "2019-01-05T15:15:18.000Z",
"name": "e30e7b9680634b2c888c8bb513cc595d.jpg",
"size": 31731,
"type": "image/jpeg",
"percent": 100,
"originFileObj": {
"uid": "rc-upload-1551084269812-5",
"width": 600,
"height": 354
},
"status": "done",
"thumbUrl": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAHQ9qKKlbimcXrIH9o2vH/AC2T+ddPj98v+9RRWsuhnHdk0ar9qb5R0Pb6VPB/qh9aKKiRr0Irnt/vUDr+NFFJCRqWxJik5Pb+dLJ938aKK06mYSdKKKKBH//Z",
"response": {
"retCode": 0,
"imgUrl": "http://qianguyihao.com/opfewfwj098902kpkpkkj976fe.jpg",
"photoid": 271850
}
}
fileList 的打印結果:
[
{
"uid": "rc-upload-1551084269812-3",
"width": 1000,
"height": 667,
"lastModified": 1501414799000,
"lastModifiedDate": "2017-07-30T11:39:59.000Z",
"name": "29381f30e924b89914e91b33.jpg",
"size": 135204,
"type": "image/jpeg",
"percent": 100,
"originFileObj": {
"uid": "rc-upload-1551084269812-3",
"width": 1000,
"height": 667
},
"status": "done",
"thumbUrl": "data:image/jpeg;base64,/E3ju1tlaK1fzJOnHQU3LsLV7HO6Zrk11MZJ7luT0A4FZuRagi9quvzQQ4iuEJ7ZpqTG4djDsPFl2Lg733f8C4q+YhQ8zoYfGSqoMmfwo5huLL0HjiyPDSYPvxRdC1XQvxeLrB8fvl/OnoLmL9vrdvvYS3NGFVe2YsASOh71JfQyrqV2mXLHOcccVSIYEnDyZO9XXB9KYH//Z",
"response": {
"retCode": 0,
"msg": "success",
"imgUrl": "http://qianguyihao.com/hfwpjouiurewnmbhepr689.jpg",
}
},
{
"uid": "rc-upload-1551084269812-5",
"width": 600,
"height": 354,
"lastModified": 1546701318000,
"lastModifiedDate": "2019-01-05T15:15:18.000Z",
"name": "e30e7b9680634b2c888c8bb513cc595d.jpg",
"size": 31731,
"type": "image/jpeg",
"percent": 100,
"originFileObj": {
"uid": "rc-upload-1551084269812-5",
"width": 600,
"height": 354
},
"status": "done",
"thumbUrl": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAHQ9qKKlbimcXrIH9o2vH/AC2T+ddPj98v+9RRWsuhnHdk0ar9qb5R0Pb6VPB/qh9aKKiRr0Irnt/vUDr+NFFJCRqWxJik5Pb+dLJ938aKK06mYSdKKKKBH//Z",
"response": {
"retCode": 0,
"imgUrl": "http://qianguyihao.com/opfewfwj098902kpkpkkj976fe.jpg",
"photoid": 271850
}
}
]
上方的json數據中,需要做的幾點解釋:
- response字段裏面的數據,請求後端接口後,後臺返回給前端數據,包含了圖片中的url鏈接
- status字段裏存放的是圖片上傳的實時狀態,包括上傳中、上傳完成、上傳失敗。
- thumbUrl字段裏面存放的是圖片的base64編碼。
這個base64編碼非常非常長。當點擊圖片預覽的時候,其實就是加載的 thumbUrl 這個字段裏的資源,難怪瀏覽器會卡死。
解決辦法:在 handleChange方法裏,圖片上傳成功後,將 thumbUrl 字段裏面的 base64 編碼改爲真實的圖片url。代碼實現如下:
handleChange = ({ file, fileList }) => {
console.log(JSON.stringify(file)); // file 是當前正在上傳的 單個 img
console.log(JSON.stringify(fileList)); // fileList 是已上傳的全部 img 列表
// 【重要】將 圖片的base64替換爲圖片的url。 這一行一定不會能少。
// 圖片上傳成功後,fileList數組中的 thumbUrl 中保存的是圖片的base64字符串,這種情況,導致的問題是:圖片上傳成功後,點擊圖片縮略圖,瀏覽器會會卡死。而下面這行代碼,可以解決該bug。
fileList.forEach(imgItem => {
if (imgItem && imgItem.status == 'done' && imgItem.response && imgItem.response.imgUrl) {
imgItem.thumbUrl = imgItem.response.imgUrl;
}
});
setFilesList(ifilList);
};