前置条件 Vue3 ali-oss
- 自行安装依赖
yarn add ali-oss
自己找个文件夹, - 然后创建 oss.ts 文件
- accessKeyId: ‘’,accessKeySecret: ‘’, 去oss管理器找
import OSS from 'ali-oss';
import { buildShortUUID } from '/@/utils/uuid';
import { urlToBase64, dataURLtoBlob } from '/@/utils/file/base64Conver';
// @ts-ignore
const isDev = ['development', 'test', 'dev'].includes(process.env.NODE_ENV);
/**
* @description 获取oss实例
* @param region 桶所在区域
* @param bucket 桶名称
*/
export function getOssClient({}: {
region?: 'DEFAULT' /* 桶所在区域 */;
bucket?: 'DEFAULT' /* 桶名称 */;
}) {
return new OSS({
region: 'oss-cn-hangzhou' /* Bucket所在地域 */,
// 从STS服务获取的临时访问密钥(AccessKey ID和AccessKey Secret)。
accessKeyId: '', //oss accessKeyId
accessKeySecret: '', // oss accessKeySecret
// 从STS服务获取的安全令牌(SecurityToken)。
// stsToken: 'yourSecurityToken',
// refreshSTSToken: async () => {
// 向您搭建的STS服务获取临时访问凭证。
// const info = await fetch('your_sts_server');
// return {
// accessKeyId: info.accessKeyId,
// accessKeySecret: info.accessKeySecret,
// stsToken: info.stsToken,
// };
// },
// refreshSTSTokenInterval: 300000/* 刷新临时访问凭证的时间间隔,单位为毫秒 */,
bucket: 'ali-dish' /* Bucket名称 */,
});
}
interface UploadFileResultModule {
name: string;
url: string;
domain: string;
fastDomain: string;
dir: string;
fileName: string;
fastUrl: string;
res: {
status: string;
statusCode: string;
headers: {
'content-length': string;
};
size: string;
aborted: boolean;
rt: string;
keepAliveSocket: boolean;
data: {
type: string;
data: any[];
};
requestUrls: string[];
timing: any;
remoteAddress: string;
remotePort: string;
};
}
/**
* @description 简单上传文件
* @param data 文件
* @param fileName 文件名称
* @param reName 是否重命名
* @param mineType 文件类型,即文件后缀
* @param dir 文件存储路径
* @param region 桶所在区域
* @param bucket 桶名称
*/
export async function uploadFile({
data,
fileName,
reName = true,
mineType,
region = 'DEFAULT',
bucket = 'DEFAULT',
}: {
data: Blob /* 文件 */;
fileName?: string /* 文件名称 */;
reName?: boolean /* 是否重命名 */;
mineType?: string /* 文件类型,即文件后缀 */;
dir?: 'FOOD_MAIN_IMG' /* 文件存储路径 */;
region?: 'DEFAULT' /* 桶所在区域 */;
bucket?: 'DEFAULT' /* 桶名称 */;
}): Promise<UploadFileResultModule> {
function getFileName(): string {
let name = '';
if (fileName && fileName.indexOf('.') > -1) {
const arr = fileName.split('.');
for (let i = 0; i < arr.length - 1; i++) {
name += arr[i];
}
name =
(reName ? buildShortUUID(name) : arr[arr.length - 2]) +
'.' +
(mineType || arr[arr.length - 1] || 'png');
} else {
name =
(reName ? buildShortUUID(new Date().getTime().toString()) : fileName) +
'.' +
(mineType || 'png');
}
return name;
}
const saveFileName = getFileName();
const fullPath = `${'index/main/'}${saveFileName}`;
const res = await getOssClient({ region, bucket }).put(fullPath, data);
return {
...res,
domain: 'http://XXX.oss-cn-hangzhou.aliyuncs.com',
fastDomain: 'http://XXX.oss-cn-hangzhou.aliyuncs.com',
dir: 'index/main/',
fileName: saveFileName,
fastUrl: 'http://XXX.oss-cn-hangzhou.aliyuncs.com' + '/' + 'index/main/' + saveFileName,
};
}
export async function uploadFileByUrl(
url: string | string[],
{
dir = 'DISH_MAIN_IMG',
fileName,
region = 'DEFAULT',
bucket = isDev ? 'DEFAULT_TEST' : 'DEFAULT',
}: {
fileName?: string /* 文件名称 */;
dir?: 'DISH_MAIN_IMG' /* 文件存储路径 */;
region?: 'DEFAULT' /* 桶所在区域 */;
bucket?: 'DEFAULT' /* 桶名称 */;
}
) {
async function single(item) {
let arr: string[] = [];
let blob;
if (item.indexOf('data:') === 0) {
arr = ['png'];
blob = dataURLtoBlob(item);
} else {
arr = item.split('.');
blob = dataURLtoBlob(await urlToBase64(item));
}
return await uploadFile({
data: blob,
dir,
fileName,
region,
bucket,
mineType: arr[arr.length - 1],
});
}
if (typeof url === 'string') {
return single(url);
} else {
const arr = new Array(url.length).fill(null);
for (let index = 0; index < url.length; index++) {
const item = url[index];
const result = await single(item);
arr[index] = result;
if (arr.indexOf(null) < 0) {
return Promise.resolve(arr);
}
}
}
}
class FfileReader extends FileReader {
file;
proportion /* 图片或视频尺寸比例 */;
maxSize /* 最大文件尺寸,单位kb */;
maxWidth /* 最大文件宽度 */;
maxHeight /* 最大文件高度 */;
imgWidth /* 最大文件宽度 */;
imgHeight /* 最大文件高度 */;
status = 0 /* 文件读取状态 */;
// loaded = null /* 读取完成后的结果 */;
message = '';
ecb = Function;
constructor(
file = File,
proportion = '',
maxSize = 0,
maxWidth = 0,
maxHeight = 0,
imgWidth = 0,
imgHeight = 0,
cb = Function,
ecb = Function
) {
super();
this.file = file;
this.proportion = proportion;
this.maxSize = maxSize;
this.maxWidth = maxWidth;
this.maxHeight = maxHeight;
this.imgWidth = imgWidth;
this.imgHeight = imgHeight;
if (ecb) {
this.ecb = ecb;
}
this.status = super.readyState;
super.onabort = this.err;
super.onerror = this.err;
super.onload = (e) => {
const size = {
width: 0,
height: 0,
};
const load = () => {
if (maxSize && e.total / 1024 > maxSize) {
this.message = `单个文件大小不能超过${maxSize / 1024}m,文件${file.name}大小为:${(
e.total /
1024 /
1024
).toFixed(2)}m`;
} else if (maxWidth && size.width && size.width > maxWidth) {
this.message = `单个文件宽度不能超过${maxWidth}像素,文件${file.name}宽度为:${size.width}像素`;
} else if (maxHeight && size.height > maxHeight) {
this.message = `单个文件高度不能超过${maxHeight}像素,文件${file.name}高度为:${size.height}像素`;
} else if (imgWidth && size.width && size.width != imgWidth) {
this.message = `图片宽度必须为${imgWidth}像素,文件${file.name}宽度为:${size.width}像素`;
} else if (imgHeight && size.height != imgHeight) {
this.message = `图片高度必须为${imgHeight}像素,文件${file.name}高度为:${size.height}像素`;
} else if (proportion && size.width && size.height) {
const arr = proportion.split(/[,.|:~`!@#$%^&*-/_/=+;/\\?><]/);
if (
arr.length == 2 &&
Number(arr[0]) &&
Number(arr[1]) &&
size.width / size.height != Number(arr[0]) / Number(arr[1])
) {
this.message = `单个文件尺寸比例为${arr[0] + ':' + arr[1]}像素,文件${
file.name
}尺寸比例为:${'1:' + (size.height / size.width).toFixed(2)}`;
}
}
if (this.message) {
// Vue.prototype.$message({
// message: this.message,
// type: "error",
// });
ecb && ecb();
super.abort();
} else if (e.target && e.target.result) {
const path = e.target.result;
cb &&
cb({
file,
path,
});
}
};
if (this.file.type.indexOf('image') > -1) {
const imageObj = new Image();
imageObj.src = String(e.target && e.target.result);
imageObj.onload = function () {
size.width = imageObj.width;
size.height = imageObj.height;
load();
};
} else if (this.file.type.indexOf('video') > -1) {
const videoUrl = URL.createObjectURL(file);
const videoObj = document.createElement('video');
videoObj.onloadedmetadata = () => {
URL.revokeObjectURL(videoUrl);
size.width = videoObj.videoWidth;
size.height = videoObj.videoHeight;
load();
};
videoObj.src = videoUrl;
videoObj.load();
} else {
load();
}
};
super.readAsDataURL(this.file);
}
err() {
this.status = -1;
this.ecb && this.ecb();
}
}
//上传文件
export function chooseFile({
cb,
max = 1,
accept = '',
proportion = '',
maxSize = 0,
maxWidth = 0,
maxHeight = 0,
imgWidth = 0,
imgHeight = 0,
loading = '',
}) {
const el = document.createElement('input');
el.setAttribute('type', 'file');
if (max > 1) {
el.setAttribute('multiple', 'true');
}
if (accept) {
el.setAttribute('accept', accept);
}
el.click();
let waiting = false; // 是否尚在等待选择文件
let clicked = false; // 按钮是否被点击
el.addEventListener('click', () => {
clicked = true; // 按钮被点击
waiting = true; // 等待用户选择文件, 此时按钮会失去焦点
});
el.addEventListener('blur', () => {
if (clicked && waiting) {
clicked = false; // 用户点击容器后, 容器会失去一次焦点, 此时处于waiting状态
// waiting没有被input的change事件置为false, 却触发了blur的失焦事件
} else if (waiting) {
// 容器再次失去焦点, 仍旧处于waiting状态, 断言用户取消了选择
console.log('blur事件测试到用户取消了选择');
}
});
el.addEventListener('change', () => {
// if (loading) {
// loading = Vue.prototype.$loading({
// target: loading,
// lock: true,
// text: "加载中...",
// spinner: "el-icon-loading",
// background: "rgba(0, 0, 0, 0.7)",
// });
// }
waiting = false; // 检测到用户选择了文件
let files = el.files || [];
const data = [];
if (files.length) {
const status = [];
if (files.length > max) {
/* 检测到用户选择文件数量超出限制,取前面符合数量的文件 */
console.log(files.length, max);
const fileList = [];
for (let i = 0; i < files.length; i++) {
const element = files[i];
if (i < max) {
fileList.push(element);
}
}
files = fileList;
// Vue.prototype.$message({
// message: `您选择的文件数量超出限制,已取前${max}个文件。`,
// type: "warn",
// });
}
for (const item of files) {
const fr = new FfileReader(
item,
proportion,
maxSize,
maxWidth,
maxHeight,
imgWidth,
imgHeight,
(e) => {
data.push(e);
if (data.length == files.length) {
cb(data, loading);
}
},
() => {
cb([], loading);
}
);
status.push(fr);
}
} else {
cb([], loading);
}
});
}
//上传图片
export function chooseImg({
cb,
max = 1,
proportion = '',
maxSize = 2048,
maxWidth = 0,
maxHeight = 0,
imgWidth = 0,
imgHeight = 0,
}) {
chooseFile({
max,
accept: 'image/*',
proportion,
maxSize,
maxWidth,
maxHeight,
imgWidth,
imgHeight,
cb,
});
}
使用chooseFile上传文件
chooseFile({
cb: (files) => {
if (files.length) {
files.forEach(async (i) => {
const results = await uploadFile({
data: i.file,
fileName: 'SERVICE' + new Date().getTime() + i.file.name,
dir: 'FILE',
});
console.log(results.url)
});
} else {
notification['error']({
message: '上传失败',
});
}
},
max: 100,
});
使用 chooseImg 上传照片
chooseImg({
cb: async (files) => {
console.log(files);
if (files.length) {
const results = await uploadFile({
data: files[0].file,
fileName: 'IMGURL' + new Date().getTime(),
dir: 'IMG',
});
console.log(results);
} else {
notification['error']({
message: '上传失败',
});
}
},
maxSize: 1024, //图片大小
imgWidth: 220, //图片宽
imgHeight: 220, //图片高
});
buildShortUUID文件
const hexList: string[] = [];
for (let i = 0; i <= 15; i++) {
hexList[i] = i.toString(16);
}
export function buildUUID(): string {
let uuid = '';
for (let i = 1; i <= 36; i++) {
if (i === 9 || i === 14 || i === 19 || i === 24) {
uuid += '-';
} else if (i === 15) {
uuid += 4;
} else if (i === 20) {
uuid += hexList[(Math.random() * 4) | 8];
} else {
uuid += hexList[(Math.random() * 16) | 0];
}
}
return uuid.replace(/-/g, '');
}
let unique = 0;
export function buildShortUUID(prefix = ''): string {
const time = Date.now();
const random = Math.floor(Math.random() * 1000000000);
unique++;
return prefix + '_' + random + unique;
}
文件转base64 base64Conver
/**
* @description: base64 转 blob
*/
export function dataURLtoBlob(base64Buf: string): Blob {
const arr = base64Buf.split(',');
const typeItem = arr[0];
const mime = typeItem.match(/:(.*?);/)![1];
const bstr = atob(arr[1]);
let n = bstr.length;
const u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
return new Blob([u8arr], { type: mime });
}
/**
* img url to base64
* @param url
*/
export function urlToBase64(url: string, mineType?: string): Promise<string> {
return new Promise((resolve, reject) => {
let canvas = document.createElement('CANVAS') as Nullable<HTMLCanvasElement>;
const ctx = canvas!.getContext('2d');
const img = new Image();
img.crossOrigin = '';
img.onload = function () {
if (!canvas || !ctx) {
return reject();
}
canvas.height = img.height;
canvas.width = img.width;
ctx.drawImage(img, 0, 0);
const dataURL = canvas.toDataURL(mineType || 'image/png');
canvas = null;
resolve(dataURL);
};
img.src = url;
});
}