基于ali-oss实现不同类型文件上传不同的bucket,并根据大小选择直接上传还是分片上传
1 配置OSS
可以看阿里云文档
ps:记得配置跨域
2 引入依赖
pnpm install ali-oss -save
3 上传核心代码
import OSS from "ali-oss";
import dayjs from "dayjs"; // 引入时间组件
import {
buildUUID } from "@pureadmin/utils";
import {
getStsToken } from "@/api/common/oss";
const BUCKET_PREFIX = "XXX";
// 获取不同文件类型bucket
const OSS_BUCKET_OBJECT = {
image: BUCKET_PREFIX + "-image",
audio: BUCKET_PREFIX + "-audio",
video: BUCKET_PREFIX + "-video1",
application: BUCKET_PREFIX + "-file",
other: BUCKET_PREFIX + "-upload"
};
// 分片的大小
const SHARD_SIZE = 5;
// 文件大小阈值,主要区分直接上传和分片上传
const FILE_SIZE_THRESHOLD = 50;
type configType = {
accessKeyId: string;
accessKeySecret: string;
bucket: string;
expiration: string;
region: string;
token: string;
stsToken: string;
};
let config: configType = undefined;
// 对文件进行上传
export async function uploadFile(file, progressCallback = () => {
}) {
// progressCallback 三个参数 (进度, 断点信息, 返回值)
// 校验传入的对象是否正确
if (!file || Object.prototype.toString.call(file) !== "[object File]") {
throw new Error("参数不正确");
}
// 根据约定和相关规则进行参数配置
const ossConfig: configType = await getOssConfig();
ossConfig.bucket = getBucket(file);
ossConfig.stsToken = ossConfig.token;
const environment = `/${
import.meta.env.VITE_LOGOGRAM}`;
const pathName = `${
environment}/${
dayjs().format("YYYY-MM-DD")}/`;
const fileName = `${
buildUUID()}.${
file.name.split(".").at(-1)}`;
// 实例化OSS对象
const client = new OSS(ossConfig);
try {
const fileSize = file.size / 1024 / 1024;
// 根据文件大小选择上传的方式
if (fileSize > FILE_SIZE_THRESHOLD) {
const option = {
partSize: 1024 * 1024 * SHARD_SIZE,
parallel: Math.ceil(fileSize / SHARD_SIZE),
progress: progressCallback
};
const result = await client.multipartUpload(
pathName + fileName,
file,
option
);
return `/${
result.name}`;
} else {
const result = await client.put(pathName + fileName, file);
return `/${
result.name}`;
}
} catch (e) {
return Promise.reject(e);
}
}
// 获取后端返回的临时凭证,并根据时间判断凭证是否过期
export async function getOssConfig() {
if (config?.expiration && dayjs().isBefore(dayjs(config.expiration))) {
return config;
} else {
const {
data } = await getStsToken();
config = data as configType;
return data;
}
}
// 根据文件类型获取不同的bucket
function getBucket(file) {
const bucket = OSS_BUCKET_OBJECT[file.type.split("/")[0]];
if (bucket) return bucket;
else return OSS_BUCKET_OBJECT["other"];
}
4 文件回显
// 访问协议
const AGREEMENT = "https://";
// 获取不同文件类型的访问地址
const SERVER_ADDRESS = {
image: "img.xxx.xxx.com",
audio: "audio.xxx.xxx.com",
video: "video.xxx.xxx.com",
application: "file.xxx.xxx.com",
other: "upload.xxx.xxx.com"
};
export const imageRegex = RegExp(/(jpg|bmp|gif|ico|pcx|jpeg|tif|png|raw|tga)/);
export const audioRegex = RegExp(/(mp3|wav|flac|ogg|aac|wma)/);
export const videoRegex = RegExp(
/(avi|wmv|mpeg|mp4|m4v|mov|asf|flv|f4v|rmvb|rm|3gp|vob)/
);
export const applicationRegex = RegExp(
/(doc|docx|xls|xlsx|ppt|pptx|pdf|txt|apk|zip)/
);
export function preview(pathName) {
const suffix = pathName.split(".").at(-1);
let type = "other";
if (imageRegex.test(suffix)) type = "image";
if (audioRegex.test(suffix)) type = "audio";
if (videoRegex.test(suffix)) type = "video";
if (applicationRegex.test(suffix)) type = "application";
return `${
AGREEMENT}${
SERVER_ADDRESS[type]}${
pathName}`;
}
deoRegex.test(suffix)) type = "video";
if (applicationRegex.test(suffix)) type = "application";
return `${
AGREEMENT}${
SERVER_ADDRESS[type]}${
pathName}`;
}
5 补充,上传增加秒传版本
import OSS from "ali-oss";
import dayjs from "dayjs"; // 引入时间组件
import cryptojs from "crypto-js";
import {
buildUUID } from "@pureadmin/utils";
import {
getStsToken, getFilePath, saveFilePath } from "@/api/common/oss";
// 获取不同文件类型bucket
const OSS_BUCKET_OBJECT = {
image: "zhima-image",
audio: "zhima-audio",
video: "zhima-video1",
application: "zhima-file",
other: "zhima-upload"
};
// 分片的大小
const SHARD_SIZE = 20;
// 文件大小阈值,主要区分直接上传和分片上传
const FILE_SIZE_THRESHOLD = 50;
type configType = {
accessKeyId: string;
accessKeySecret: string;
bucket: string;
expiration: string;
region: string;
token: string;
stsToken: string;
};
let config: configType = undefined;
// 对文件进行上传
export async function uploadFile(file, progressCallback = () => {
}) {
try {
// progressCallback 三个参数 (进度, 断点信息, 返回值)
// 校验传入的对象是否正确
if (!file || Object.prototype.toString.call(file) !== "[object File]") {
throw new Error("参数不正确");
}
const md5 = await toMD5(file);
// 进行秒传的判定
const loadPath = await judgmentExistence(md5);
if (loadPath) {
return loadPath;
}
// 根据约定和相关规则进行参数配置
const ossConfig: configType = await getOssConfig();
ossConfig.bucket = getBucket(file);
ossConfig.stsToken = ossConfig.token;
const environment = `/${
import.meta.env.VITE_LOGOGRAM}`;
const pathName = `${
environment}/${
dayjs().format("YYYY-MM-DD")}/`;
const fileName = `${
buildUUID()}.${
file.name.split(".").at(-1)}`;
// 实例化OSS对象
const client = new OSS(ossConfig);
const fileSize = file.size / 1024 / 1024;
// 根据文件大小选择上传的方式
if (fileSize > FILE_SIZE_THRESHOLD) {
const option = {
partSize: 1024 * 1024 * SHARD_SIZE,
parallel: Math.ceil(fileSize / SHARD_SIZE),
progress: progressCallback,
timeout: 1000 * 60 * 30
};
const result = await client.multipartUpload(
pathName + fileName,
file,
option
);
await saveFilePath({
fileMd5: md5,
path: `/${
result.name}`
});
return `/${
result.name}`;
} else {
const result = await client.put(pathName + fileName, file);
await saveFilePath({
fileMd5: md5,
path: `/${
result.name}`
});
return `/${
result.name}`;
}
} catch (e) {
console.error(e);
return Promise.reject(e);
}
}
// 获取后端返回的临时凭证,并根据时间判断凭证是否过期
export async function getOssConfig() {
if (config?.expiration && dayjs().isBefore(dayjs(config.expiration))) {
return config;
} else {
const {
data } = await getStsToken();
config = data as configType;
return data;
}
}
// 根据文件类型获取不同的bucket
function getBucket(file) {
const bucket = OSS_BUCKET_OBJECT[file.type.split("/")[0]];
if (bucket) return bucket;
else return OSS_BUCKET_OBJECT["other"];
}
// 校验是都可以秒传
async function judgmentExistence(md5) {
try {
const {
data } = await getFilePath({
fileMd5: md5 });
return data;
} catch (e) {
console.error(e);
return "";
}
}
async function toMD5(file) {
return new Promise((resolve, reject) => {
try {
const reader = new FileReader();
// 文件太大会卡死
if (file.size > 1024 * 1024 * 50) {
file = file.slice(0, 1024 * 1024 * 50);
}
reader.readAsDataURL(file);
// 开始转base64
reader.onload = async () => {
const md5 = cryptojs.MD5(reader.result.toString()).toString();
resolve(md5);
};
} catch (e) {
console.error(e);
reject(e);
}
});
}
文章评论