在现代Web应用开发中,用户界面的交互性和美观性至关重要。特别是对于个人资料页面,一个简洁且功能完备的头像上传组件可以显著提升用户体验。本文将介绍如何使用React结合Ant Design来构建一个支持图片预览、水印添加以及上传进度提示的头像上传组件。
技术栈
- React: 负责组件化开发。
- Ant Design: 提供了丰富的UI组件库。
- FileReader API: 用于读取文件内容并转换为Base64字符串。
功能亮点
- 图片格式验证: 只允许上传JPEG或PNG格式的图片。
- 大小限制: 图片大小不得超过2MB。
- 上传进度指示: 显示上传状态(如加载中、上传完成)。
- 图片预览: 实时预览已选择的图片。
- 水印添加: 在上传前自动给图片添加指定文字水印
代码解析
首先,我们导入必要的React Hook以及Ant Design提供的组件:
import React, { useEffect, useState } from 'react';
import { LoadingOutlined, PlusOutlined ,UploadOutlined } from '@ant-design/icons';
import { Flex, message, Upload, Avatar, List } from 'antd';
const props = {
action: 'http://xxx.xx.76.143:48080/admin-api/system/user/profile/update-avatar',
listType: 'picture-circle',
name:"avatarFile",
headers: {
'Authorization':'Bearer '+ localStorage.getItem('token'),
'tenant-id': '1'
},
beforeUpload(file) {
return new Promise((resolve) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => {
const img = document.createElement('img');
img.src = reader.result;
img.onload = () => {
const canvas = document.createElement('canvas');
canvas.width = img.naturalWidth;
canvas.height = img.naturalHeight;
const ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0);
ctx.fillStyle = '#fff';
ctx.textBaseline = 'middle';
ctx.font = '23px Arial';
ctx.fillText('版权归属woods', 20, 20);
canvas.toBlob((result) => resolve(result));
};
};
});
},
};
const beforeUpload = (file) => {
const isJpgOrPng = file.type === 'image/jpeg' || file.type === 'image/png';
if (!isJpgOrPng) {
message.error('You can only upload JPG/PNG file!');
}
const isLt2M = file.size / 1024 / 1024 < 2;
if (!isLt2M) {
message.error('Image must smaller than 2MB!');
}
return isJpgOrPng && isLt2M;
};
const UserInfo = (userData) => {
const [loading, setLoading] = useState(false);
const [imageUrl, setImageUrl] = useState('');
useEffect(() => {
setImageUrl(userData.data.avatar)
}, [userData])
const handleChange = (info) => {
if (info.file.status === 'uploading') {
setLoading(true);
return;
}
if (info.file.status === 'done') {
}
};
const uploadButton = (
<button
style={{
border: 0,
background: 'none',
}}
type="button"
>
{loading ? <LoadingOutlined /> : <PlusOutlined />}
<div
style={{
marginTop: 8,
}}
>
Upload
</div>
</button>
);
return (
<div>
<Flex gap="middle" wrap>
<Upload
name="avatarFile"
listType="picture-circle"
className="avatar-uploader"
showUploadList={false}
action="http://xxx.xx.76.143:48080/admin-api/system/user/profile/update-avatar"
beforeUpload={beforeUpload}
onChange={handleChange}
headers={{
'Authorization':'Bearer '+ localStorage.getItem('token'),
'tenant-id': '1'
}}
>
{imageUrl ? (
<img src={imageUrl} alt="avatar" style={{ width: '100%', }} />
) : ( uploadButton )}
</Upload>
<Upload {...props}>
{imageUrl ? (
<img src={imageUrl} alt="avatar" style={{ width: '100%', }} />
) : ( uploadButton )}
</Upload>
</Flex>
</div>
);
};
export default UserInfo;
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
- 60.
- 61.
- 62.
- 63.
- 64.
- 65.
- 66.
- 67.
- 68.
- 69.
- 70.
- 71.
- 72.
- 73.
- 74.
- 75.
- 76.
- 77.
- 78.
- 79.
- 80.
- 81.
- 82.
- 83.
- 84.
- 85.
- 86.
- 87.
- 88.
- 89.
- 90.
- 91.
- 92.
- 93.
- 94.
- 95.
- 96.
- 97.
- 98.
- 99.
- 100.
- 101.
- 102.
- 103.
- 104.
- 105.
- 106.
- 107.
- 108.
- 109.
- 110.
- 111.
- 112.
两种上传文件写法,都能上传图片
,