这个组件目前还不是很完善 比如自定义占位
组件是老早就写了 但是一直有异常问题没有处理 今天处理了一下oss授权参数过期的情况 但是还是感觉不完美 求意识到的大佬点播
贴下源码
// AliyunOSSUpload.tsx
import { Modal, Upload, UploadFile, UploadProps } from "antd";
import Icon from "@/components/Icon";
import { message } from "@/hooks/useTips";
import { getBase64 } from "@/utils/utils";
import { ReactNode, useEffect, useState } from "react";
import { type OssParams, getFilename, getOSSParams, getYMDStr } from "@/utils/upload";
type Props = {
fileList: UploadFile[];
accept?: string;
maxLength?: number;
children?: ReactNode;
onChange: (list: UploadFile[]) => void;
};
const AliOssUpload = (props: Props) => {
const { fileList, accept = "image/*", maxLength = 10, onChange } = props;
const [ossData, setOssData] = useState<OssParams>();
const [previewOpen, setPreviewOpen] = useState(false);
const [previewImage, setPreviewImage] = useState("");
const [previewTitle, setPreviewTitle] = useState("");
const handleChange: UploadProps["onChange"] = ({ file, fileList }) => {
let files = [...fileList];
const url = `${ossData?.host}/${ossData?.dir}${getYMDStr()}/${file.fileName}`;
if (file.status === "done") {
file.url = url;
}
if (file.status === "error") {
files = files.filter(item => item.status !== "error");
message.error("上传失败,请稍后重试");
}
onChange(files);
};
const getExtraData: UploadProps["data"] = file => {
const subDir = `${getYMDStr()}/`;
const dir = ossData?.dir;
// 传递给后端的额外参数
return {
key: `${dir}${subDir}${file.fileName}`,
OSSAccessKeyId: ossData?.accessId,
policy: ossData?.policy,
success_action_status: "200",
Signature: ossData?.signature
};
};
const handleBeforeUpload: UploadProps["beforeUpload"] = async (file: File) => {
await fetchOSSParams();
const isLt5M = file.size / 1024 / 1024 < 5;
return new Promise(resolve => {
if (!isLt5M) {
message.error("图片大小不能超过5M");
return false;
} else {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
file.fileName = getFilename(file.name);
return resolve(true);
}
});
};
const handlePreview = async (file: UploadFile) => {
if (!file.url && !file.preview) {
file.preview = await getBase64(file.originFileObj as File);
}
setPreviewImage(file.url || (file.preview as string));
setPreviewOpen(true);
setPreviewTitle(file.name || file.url!.substring(file.url!.lastIndexOf("/") + 1));
};
const handleCancel = () => setPreviewOpen(false);
const uploadProps = {
name: "file",
fileList: fileList,
action: ossData?.host,
accept: accept,
onChange: handleChange,
data: getExtraData,
beforeUpload: handleBeforeUpload,
onPreview: handlePreview
};
const fetchOSSParams = async () => {
const ossParams = await getOSSParams();
setOssData(ossParams);
};
useEffect(() => {
fetchOSSParams();
}, []);
return (
<>
<Upload {...uploadProps} listType="picture-card" multiple={true} fileList={fileList}>
{fileList.length >= maxLength ? null : (
<div>
<Icon name="PlusOutlined" antd />
<div className="mt-2">上传</div>
</div>
)}
</Upload>
<Modal open={previewOpen} title={previewTitle} footer={null} onCancel={handleCancel}>
<img alt="example" className="w-full" src={previewImage} />
</Modal>
</>
);
};
export default AliOssUpload;
// upload.ts
import commonApi from "@/api/interface/common";
import { localStore } from "./storage";
import { OSS_PARAMS_KEY } from "@/const";
export type OssParams = {
accessId: number;
host: string;
policy: string;
signature: string;
dir: string;
expire: number;
};
export const getOSSParams = async () => {
const KEY = OSS_PARAMS_KEY;
let ossParams = localStore.get(KEY);
const expired = ossParams && ossParams?.expire < Date.now();
if (!ossParams || expired) {
const res = await commonApi.getOSSParams();
ossParams = {
accessId: res.accessKeyId,
host: res.accessURL,
policy: res.securityToken,
signature: res.signature,
dir: res.dir,
expire: res.expiresAt
};
localStore.set(KEY, ossParams);
}
return ossParams;
};
const getFileSuffix = (fileName: string) => {
const pos = fileName.lastIndexOf(".");
let suffix = "";
if (pos !== -1) {
suffix = fileName.substring(pos);
}
return suffix;
};
export const genRandomStr = (length: number) => {
const chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
const charLength = chars.length;
let result = "";
for (let i = 0; i < length; i++) {
result += chars.charAt(Math.floor(Math.random() * charLength));
}
return result;
};
export const getYMDStr = () => {
const now = new Date();
const YYYY = now.getFullYear();
const MM = now.getMonth() + 1;
const DD = now.getDate();
return `${YYYY}${MM > 9 ? MM : `0${MM}`}${DD > 9 ? DD : `0${DD}`}`;
};
export const getFilename = (filePath: string) => {
return `${genRandomStr(8)}-${genRandomStr(8)}-${genRandomStr(8)}${getFileSuffix(filePath)}`;
};
export function downloadURLFile(url: string) {
const element = document.createElement("a");
element.download = url;
element.style.display = "none";
element.href = url;
document.body.appendChild(element);
element.click();
document.body.removeChild(element);
}