介绍
在表单功能中,图片,文件上传可以说是非常常见,前端如何快速的传递给后端,通常而言,会直接将 图片/文件地址发送给后端
在这里就需要小伙伴先配置 OSS了,具体的文档请看 配置OSS ,这里就不做多的介绍了(可关闭OSS配置)
为什么要封装呢?
首先这块的功能是比较常见的,比如文件的类型,文件的大小,图片的裁剪,文件的数量,展示的类型等,做了一层封装,以便更好的使用~
这个组件主要使用了 Ant Design 的 Upload 和 react-cropper
为什么不用 antd-img-crop 呢?原因是它不具备自定义裁剪
在线演示:图片文件上传
功能
当你有以下需求时,可以试试这个组件:
- 需要使用 OSS 将文件转化为网络地址
- 需要设置上传的大小,类型,是否重复上传等功能
- 图片需要自定义裁剪的功能~
代码演示
功能演示
基础功能
其他格式

图片剪辑

具体代码
文件位置:src/components/OssUpLoad
全局配置文件:src/utils/Setting/OssUpLoadSy
import React, { useEffect } from 'react';import { Upload, message, Modal, Button } from 'antd';import { PlusOutlined, UploadOutlined } from '@ant-design/icons';import { OssUpLoadSy } from '@/utils/Setting'import { useState, useRef } from 'react';import Cropper from 'react-cropper';import 'cropperjs/dist/cropper.css';import Props from './interface.d';import './index.less';let aliOSS = require('ali-oss');let client = new aliOSS({region: OssUpLoadSy.OSS.region,accessKeyId: OssUpLoadSy.OSS.accessKeyId,accessKeySecret: OssUpLoadSy.OSS.accessKeySecret,bucket: OssUpLoadSy.OSS.bucket,});const OssUpLoad: React.FC<Props> = ({amount = OssUpLoadSy.amount,OSS = OssUpLoadSy.open,rules = {},listType = OssUpLoadSy.listType,onRemove,children,getFiles,crop,_config = {},button = {},initFile = [],cropConfig = {},...props}) => {const cropperRef = useRef<any>(null)const [fileList, setFileList] = useState<Array<any>>([]); //总文件数组const [getFilesList, setGetFilesList] = useState<Array<any>>([]); //总文件数组const [previewVisible, setPreviewVisible] = useState<boolean>(false); // 是否打开弹出框const [isFileFlag, setIsFileFlag] = useState<boolean>(false); // 控制照片,如果不满足则不添加const [previewTitle, setPreviewTitle] = useState<any>(''); // 图片名称const [previewImage, setPreviewImage] = useState<any>(''); // 图片展示的数据const [src, setSrc] = useState<any>(false) //裁剪图片const [file, setFile] = useState<any>(false)useEffect(() => {if(initFile.length !== 0){let fileList: Array<{url: string, name: string, uid?: number | string}> = []initFile.map((item, index) => {const param = OSS ? { newFile: item } : {}if(typeof item === 'string'){fileList = [...fileList, { url: item, name: `图片`, ...param}]}else {fileList = [...fileList, {...item, url: item?.url || '', uid: item?.uid || undefined, name: item?.name || `图片`, ...param}]}})setGetFilesList([...fileList])setFileList(fileList)}}, [])// 预览const handlePreview = async (file: any) => {if (file.type && file.type.indexOf('image') === -1) return;if (!file.url && !file.preview) {file.preview = await getBase64(file.originFileObj);}setPreviewVisible(true);setPreviewImage(file.url || file.preview);setPreviewTitle(file.name || file.url.substring(file.url.lastIndexOf('/') + 1));};// 上传前的操作const beforeUpload = async (file: any) => {let flag = true; // 控制最终的类型// 检测是否有相同类型if (!_config.noCheck && flag) {const repeat = fileList.filter((item) => item.name === file.name && item.size === file.size);if (repeat.length !== 0) {message.error('您已上传过此文件,请勿重复上传');flag = false;}}// 判断文件类型if (typeof rules.type === 'string' && flag) {const type = rules.type.trim() === 'jpg' ? 'jpeg' : rules.type.trim();if(rules.type === 'xlsx' || rules.type === 'xls') {// 单独处理 excelconst fileType = file.name.split('.').pop();if(['xlsx', 'xls'].indexOf(fileType) < 0) {message.error(rules.typeMsg || '请上传xlsx或xls格式文件');flag = false}} else if (file.type.indexOf(type) === -1) {message.error(rules.typeMsg || '请上传正确的文件类型');flag = false;}} else if (Array.isArray(rules.type) && flag) {let allFlag = false;rules.type.map((item) => {const type = item.trim() === 'jpg' ? 'jpeg' : item.trim();if(item === 'xlsx' || item === 'xls'){const fileType = file.name.split('.').pop();if(['xlsx', 'xls'].indexOf(fileType) !== -1) {allFlag = true}} else if (file.type.indexOf(type) !== -1) {allFlag = true;return;}});if (!allFlag) {message.error(rules.typeMsg || '请上传正确的文件类型');flag = false;}}// 根据listType来进行判断if (listType === 'picture-card' && file.type.indexOf('image') === -1 && flag) {message.error(_config.pictureCardTip || '请上传正确的图片类型!');flag = false;}// 判断文件大小if (rules.size && flag) {const fileSize = file.size / 1024 / 1024 < rules.size;if (!fileSize) {message.error(rules.sizeMsg || `上传文件大于${rules.size}M!请重新上传`);flag = false;}}// 裁剪if(crop){const fileReader = new FileReader()fileReader.onload = (e:any) => {const dataURL = e.target.result;setSrc(dataURL)}fileReader.readAsDataURL(file)setFile(file)return;}if (flag) onGetFiles(file);setIsFileFlag(flag);return file;};// 文件获取const onGetFiles = async (file: any) => {if (getFiles) {let result: any = file;let OssList: Array<any> = [];let fileBase64:any = ''if (OSS) {const suffix = file.name.slice(file.name.lastIndexOf('.'));const filename = _config?.ossText ? _config.ossText + suffix : suffix;const res = await client.put(`${_config.ossUrl || OssUpLoadSy.OssUrl}/${Date.now() + filename}`,file,);result = res.url;}else{fileBase64 = await getBase64(file)}result = [...getFilesList, OSS ? { file, newFile: result } : { file, fileBase64 }];OSS ? result.map((item: any) => (OssList = [...OssList, item.newFile])) : '';setGetFilesList(result);getFiles(OSS ? OssList : result, true);}};// 自定义按钮样式const uploadButton = () => {if (_config?.uploadNode) {return typeof _config.uploadNode === 'function' ? _config.uploadNode() : _config.uploadNode;}return (<div><PlusOutlined /><div style={{ marginTop: 8 }}>{_config.text || OssUpLoadSy._config.text}</div></div>);};return (<div className="UpLoadComponents"><Upload{...props}listType={listType}fileList={fileList}onPreview={handlePreview}onChange={({ fileList }) => {if (isFileFlag) setFileList(fileList);}}multiple={!_config.radio && amount !== 1}onRemove={ (file) => {const result = fileList.filter((item) => item.uid !== file.uid);setFileList(result);if (getFiles) {let getFileResult:any = getFilesList.filter((item) => item.file ? item.file.uid !== file.uid : item.uid !== file.uid);let OSSList:any = []if(OSS && getFileResult.length !== 0) {getFileResult.map((item:any) => {OSSList = [...OSSList, item?.newFile]})}setGetFilesList(getFileResult);// 删除文件getFiles(OSS ? OSSList : getFileResult, false);}if (onRemove) onRemove(file);}}beforeUpload={beforeUpload}maxCount={amount}>{listType === 'picture-card' ? (fileList.length >= amount ? null : (uploadButton())) : children ? (children) : (<Button{...button}icon={button.icon || <UploadOutlined />}type={button.type || 'primary'}disabled={fileList.length === amount}onClick={(ev) => {if (button.onClick) button.onClick(ev);}}>{_config.text || 'Upload'}</Button>)}</Upload><Modalvisible={previewVisible}title={previewTitle}footer={null}onCancel={() => setPreviewVisible(false)}><img alt="example" style={{ width: '100%' }} src={previewImage} /></Modal><ModaldestroyOnClosetitle={cropConfig?.title || OssUpLoadSy.crop.title}visible={src ? true : false}style={{height: 200}}onCancel={() => {setSrc(false);setFile(false)}}footer={null}><Croppersrc={src}ref={cropperRef}style={{height: 400}}zoomable={false}guides={false}/><div style={{marginTop: 30, display: 'flex',justifyContent: 'flex-end'}}><Button onClick={() => setSrc(false)} {...cropConfig?.cancelProps} style={{marginRight: 20, ...OssUpLoadSy.crop.cancelStyle, ...cropConfig?.cancelStyle}}>{cropConfig?.cancelText || OssUpLoadSy.crop.cancelText}</Button><Buttontype="primary"{...cropConfig?.cropProps}style={{ ...OssUpLoadSy.crop.cropStyle, ...cropConfig?.cropStyle }}onClick={async () =>{cropperRef?.current?.cropper?.getCroppedCanvas().toBlob(async (blob:any) => {const base64 = await getBase64(blob)const newFile:any = new File([blob], file.name, {type: file.type})newFile.uid = file.uidonGetFiles(newFile)setFileList([...fileList, { url: base64, uid: file.uid, name: file.name}])setFile(false)})setSrc(false)}}>{cropConfig?.cropText || OssUpLoadSy.crop.cropText}</Button></div></Modal></div>);};export default OssUpLoad;const getBase64 = (file: any) => {return new Promise((resolve, reject) => {const reader = new FileReader();reader.readAsDataURL(file);reader.onload = () => resolve(reader.result);reader.onerror = (error) => reject(error);});};
如何使用
import { OssUpLoad } from '@/components';<OssUpLoad...propsgetFiles={(file: Array<any>, flag) => {}} //获取文件/>
结果
开启 OSS的结果
不开启OSS的结果
特殊说明
- 全局配置文件可根据项目的需求去做对应的配置
- OSSUpLoad 组件本身是单独封装,在表单中的使用需要与 动态表单 结合使用
- 开启 OSS 功能,返回的则直接是地址的数组,否则有一个文件对象,和Base64的地址
- 上传的地址都需要使用
**getFiles**来获取
