开始之前需要封装一个上传的方法fileUtils.ts
import { message } from 'antd';
import Base64 from 'crypto-js/enc-base64';
import Utf8 from 'crypto-js/enc-utf8';
import HmacSHA1 from 'crypto-js/hmac-sha1';
import { request } from 'umi';const isDev = process.env.NODE_ENV === 'development';export namespace FileUtil {const env = {timeout: 10000,uploadHost: 'https://hopeman.oss-cn-beijing.aliyuncs.com/',};const genPolicy = () => {const date = new Date();date.setHours(date.getHours() + env.timeout);const srcT = date.toISOString();const policyText = {expiration: srcT,conditions: [['content-length-range', 0, 1 * 1024 * 1024 * 1024], // 设置上传文件的大小限制1G],};const rawStr = JSON.stringify(policyText);const wordArray = Utf8.parse(rawStr);const policyBase64 = Base64.stringify(wordArray);return policyBase64;};const genSignature = (policyBase64: string, accessKey: string) => {const byte = HmacSHA1(policyBase64, accessKey);const signature = Base64.stringify(byte);return signature;};export const upload = async (options: any, dir?: string): Promise<string> => {const fileInfo = options.file;const { onError = () => {}, onSuccess = () => {}, fullHost = '', ext = false } = options;return new Promise(async (resolve) => {const res = await request('/common/ram/assumeRole', {method: 'POST',});if (res?.code === 200 && res?.data) {const {Credentials: { AccessKeyId, AccessKeySecret, SecurityToken },} = res.data;const fileNameSplit = fileInfo?.name.split('.') || [];const aliyunFileKey = `${dir ?? 'excel'}/event_${new Date().getTime()}${ext ? '.' + fileNameSplit[fileNameSplit?.length - 1] : ''}`; //文件命名const policyBase64 = genPolicy();const signature = genSignature(policyBase64, AccessKeySecret);const data = new FormData();data.append('key', aliyunFileKey);data.append('policy', policyBase64);data.append('OSSAccessKeyId', AccessKeyId);data.append('signature', signature);data.append('x-oss-security-token', SecurityToken);data.append('file', fileInfo);data.append('success_action_status', '200');try {await request(isDev ? '/upload' : UPLOAD_HOST, {method: 'POST',data,});const url = `${fullHost}${aliyunFileKey}`;onSuccess(url);resolve(url);} catch (e) {onError(options);resolve('');}} else {message.error('上传失败');onError(options);resolve('');}});};export const base64CoverFile = (dataUrl: string) => {/*** base64 转 File* @param data*/const arr = dataUrl.split(','),mime = arr?.[0]?.match(/:(.*?);/)?.[1],bstr = atob(arr[1]);let n = bstr.length;const u8arr = new Uint8Array(n);while (n--) {u8arr[n] = bstr.charCodeAt(n);}const blob = new Blob([u8arr], { type: mime });// blob.lastModifiedDate = new Date(); // 文件最后的修改日期// blob.name = fileName; // 文件名return new File([blob], Date.now().toString(), { type: blob.type, lastModified: Date.now() });};// 浏览器本页下载文件export const downFileByUrl = (url: string) => {if (!url || url.length === 0) {message.error('url 不存在');return;}const a = document.createElement('a');a.setAttribute('href', url);document.body.appendChild(a);a.click();document.body.removeChild(a);};export const downMultipleFileByUrl = (urls: string[]) => {if (!urls || urls.length === 0) {message.error('url 不存在');return;}for (let i = 0; i < urls.length; i++) {const iframe = document.createElement('iframe');iframe.style.display = 'none'; // 防止影响页面// @ts-ignoreiframe.style.height = 0; // 防止影响页面iframe.src = urls[i];document.body.appendChild(iframe);// 5分钟之后删除setTimeout(() => {iframe.remove();}, 5 * 60 * 1000);}};
}
//AliyunOssUpload 组件
import { FileUtil } from '**/fileUtils';
import { PlusOutlined, UploadOutlined } from '@ant-design/icons';
import type { UploadProps } from 'antd';
import { Button, Image, message, Upload } from 'antd';
import type { RcFile } from 'antd/lib/upload';
import type { UploadFile } from 'antd/lib/upload/interface';
import filesize from 'filesize';
import type { FC, ReactElement } from 'react';
import React, { Fragment, useState } from 'react';
import styles from './styles.less';export type AliyunOssUploadProps = UploadProps & {value?: any;fullHost?: string | boolean; // 阿里云的Host,默认只返回目录,当 true 时,默认的是 UPLOAD_HOSTlimitSize?: number; // 文件大小限制单位 bytes(B) 默认是 2 * 1024 * 1024(2M) 0 表示不限制ext?: boolean; // 阿里云文件路径中是否包含文类后续名, 默认不包含文件类型后缀buttonRef?: any;readOnly?: boolean;uploadButtonRender?: ReactElement;maxCount?: number;
};const UploadImageButton = React.forwardRef((props, ref: any) => {return (<div ref={ref} className={'aliyunUpBtn'}><PlusOutlined className={'aliyunUpIcon'} /><div className={'aliyunUpTitle'} style={{ marginTop: 8 }}>上传图片</div></div>);
});const UploadNormalButton = React.forwardRef((props, ref: any) => (<Button ref={ref} icon={<UploadOutlined />}>点击上传</Button>
));const AliyunOssUpload: FC<AliyunOssUploadProps> = (props) => {const [previewVisible, setPreviewVisible] = useState(false);const [previewSrc, setPreviewSrc] = useState('');let { fullHost = '' } = props;const {ext = false,disabled,buttonRef,readOnly = false,maxCount = 1,uploadButtonRender,} = props;fullHost = typeof fullHost === 'boolean' ? (fullHost ? UPLOAD_HOST : '') : fullHost;const uploadProps: UploadProps = {listType: 'picture-card',headers: {authorization: 'authorization-text',},maxCount: maxCount,customRequest: (options) => FileUtil.upload({ ...options, fullHost, ext }),onPreview: (file: UploadFile) => {const src = file?.response || file?.thumbUrl;if (src) {setPreviewSrc(src);setPreviewVisible(true);}},defaultFileList: props?.value?.fileList?.map((item: any) => ({uid: item?.uid,name: item?.url,url: item?.url,response: item?.url,thumbUrl: item?.url,})),beforeUpload: async (file: RcFile) => {const initAccept = props?.accept || 'image/png,image/jpg,image/jpeg';const accept = initAccept.split(',');const limitSize = props?.limitSize ?? 1024 * 1024 * 2;const isValidType = accept?.includes(file.type);const isValidSize = limitSize ? file.size < limitSize : true;if (!isValidType) {await message.error(`仅支持 ${initAccept} 格式`);}if (!isValidSize) {await message.error(`图片大小不能超过 ${filesize(limitSize, { base: 2, standard: 'jedec' })}`,);}return (isValidType && isValidSize) || Upload.LIST_IGNORE;},showUploadList: { showRemoveIcon: !readOnly },...props,};const uploadButton = () => {if (readOnly || disabled) {return;}if (uploadProps?.maxCount) {if (props?.value?.fileList?.length >= uploadProps.maxCount) {return null;} else {if (uploadButtonRender) return uploadButtonRender;return props.listType === 'picture-card' || 'picture' ? (<UploadImageButton ref={buttonRef} />) : (<UploadNormalButton ref={buttonRef} />);}} else {if (uploadButtonRender) return uploadButtonRender;return props.listType === 'picture-card' || 'picture' ? (<UploadImageButton ref={buttonRef} />) : (<UploadNormalButton ref={buttonRef} />);}};if (!props.value?.fileList?.length && disabled) return <>-</>;return (<Fragment><Upload className={styles.uploadBox} {...uploadProps}>{uploadButton()}</Upload><div style={{ fontSize: 0, position: 'absolute' }}><Imagewidth={200}style={{ display: 'none' }}src={previewSrc}preview={{visible: previewVisible,src: previewSrc,onVisibleChange: (val) => {setPreviewVisible(val);},}}/></div></Fragment>);
};export default AliyunOssUpload;
在页面中使用 时
<AliyunOssUploadaccept={'image/png,image/jpg,image/jpeg'}fullHostmaxCount={5} //根据业务需求填写listType={'picture-card'}limitSize={1024 * 1024 * 5} //根据业务需求填写/>