开始之前需要封装一个上传的方法fileUtils.ts
javascript
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-ignore
iframe.style.height = 0; // 防止影响页面
iframe.src = urls[i];
document.body.appendChild(iframe);
// 5分钟之后删除
setTimeout(() => {
iframe.remove();
}, 5 * 60 * 1000);
}
};
}
javascript
//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_HOST
limitSize?: 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' }}>
<Image
width={200}
style={{ display: 'none' }}
src={previewSrc}
preview={{
visible: previewVisible,
src: previewSrc,
onVisibleChange: (val) => {
setPreviewVisible(val);
},
}}
/>
</div>
</Fragment>
);
};
export default AliyunOssUpload;
在页面中使用 时
javascript
<AliyunOssUpload
accept={'image/png,image/jpg,image/jpeg'}
fullHost
maxCount={5} //根据业务需求填写
listType={'picture-card'}
limitSize={1024 * 1024 * 5} //根据业务需求填写
/>