项目背景
vue3从发布以来,已经有一段时间,目前vue3的生态也是趋于稳定;自vue3的发布之后,市面上的许多UI组件库也是相应的进一步与vue3进行同步的更新以便更好的兼容vue3的生态。今天我要说的,其实就是我们在做开发时经常遇到的一个功能模块,那就是文件的批量上传。我相信刚开始接触文件批量这一功能模块时会碰到的一个问题,那就是当我获取到所有文件流后,进一步对获取到的文件流进行封装并上传到后端服务时,因为文件流过大导致无法进行上传成功。那么这时就会考虑使用例如像阿里的云服务oss进行上传文件,让oss在中间做一层分发的情况,等oss帮我们处理完文件流后成功的返回给我们文件的相关信息与地址。接下来我演示下功能模块的实现,功能比较粗陋,如果路人技术大神看到了还望指点一二!!!
相关的技术栈
vue3:vue3官网
element-puls:一个 Vue 3 UI 框架 | Element Plus
axios:axios中文网|axios API 中文文档 | axios
阿里云服务OSS:安装 - 对象存储 OSS - 阿里云
TypeScript:TypeScript中文网 · TypeScript—JavaScript的超集
相关业务功能的实现流程
html代码实现:
使用element-puls组件库的el-form组件、el-upload组件与el-dialog组件,实现客户端界面的展现
<template>
<el-dialog :title="'文件批量'" v-model="isShowDialog">
<el-form :model="form" ref="formRef" :rules="rules" size="default">
<el-form-item label="文件上传">
<el-upload
ref="upload"
:limit="50"
:on-exceed="handleExceed"
:on-change="handleChange"
:auto-upload="false"
:show-file-list="true"
multiple
>
<template #trigger>
<el-button type="primary">选择文件</el-button>
</template>
</el-upload>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="onCancel" size="default">取 消</el-button>
<el-button type="primary" @click="onSubmit" size="default">上 传</el-button>
</span>
</template>
</el-dialog>
</template>
相关element-puls的组件API属性可以查看文档介绍:Upload 上传 | Element Plus
业务逻辑实现:
(1).使用vue3组合API绑定并定义数据结构,TypeScript定义数据接口;
(2).点击取消时弹窗不显示,点击上传文件按钮时实现对相关文件流的获取数据结构的封装并将文件流保存;
(3).将文件流上传至阿里的oss服务,根据阿里oss文档封装oss上传方法,上传oss时自定义文件名径路并对文件名径路进行封装处理;
(4).成功上传到oss后,将返回的文件信息进行保存;将保存文件数据流通过相应的业务接口上传至后端服务,并在后端服务进一步处理再返回结构到客户端
<script lang="ts">
import { defineComponent, ref, toRefs, reactive, unref } from 'vue';
import type { ElMessage, UploadInstance, UploadProps, UploadRawFile } from "element-plus";
import { client, options } from '/@/utils/OSS';
import { random } from '/@/utils/randomUser';
interface File {
file: object;
list: object;
};
interface State {
isShowDialog: Boolean;
uploadFile: File;
};
export default defineComponent({
setup(props,{emit}) {
const upload = ref<UploadInstance>();
const formRef = ref<HTMLElement | null>(null);
const state = reactive<State>({
isShowDialog: false,
uploadFile: {
file: [],
list: []
}
});
// 关闭弹窗
const closeDialog = () => {
state.isShowDialog = false;
};
// 取消
const onCancel = () => {
closeDialog();
};
// 上传文件时触发
const handleChange: UploadProps['onChange'] = (file,files) => {
state.uploadFile.file.push(file.raw);
};
// 将文件流上传oss
const onSubmit = () => {
const formWrap = unref(formRef) as any;
if (!formWrap) return;
formWrap.validate((valid: boolean) => {
if (valid) {
let data = new Date();
let Y = data.getFullYear();
let M = (data.getMonth() + 1);
let D = data.getDate();
let tiem = Y + '-' + M + '-' + D;
let list:any = [];
// 文件批量上传
state.fileForm.file.forEach((item:any,index:any) => {
// 自定义文件名路径
let fileName = '/crm/' + tiem + '/' + random('' ,10) + '.' + item.name.split('.')[1];
// 请求oss接口上传
client.put(fileName, item, {...options}).then((response:any) => {
// console.log(response);
if (response.res.statusCode == 200) {
list.push({
fileName: item.name,
fileSize: item.size,
fileType: item.type,
fileUrl: response.url
})
getList(list); // 获取oss返回数据
upload.value!.clearFiles(); // 上传成功后清空上传信息数据
};
}).catch((error:any) => {
console.log(error); // 错误返回
});
});
}
})
}
// 获取oss返回数据并将数据保存至后端服务进行处理保存
async function getList(data:any) {
state.uploadFile.list = data;
if (data.length == state.uploadFile.file.length) {
...业务接口请求,将文件流数据返回给后端服务那边
}
};
return {
upload,
formRef,
state,
closeDialog,
onCancel,
handleChange,
onSubmit
};
}
});
</script>
将文件流上传至阿里的oss服务,根据阿里oss文档封装oss上传方法
// 封装oss方法
import OSS from 'ali-oss';
// 实例化OSS,并配置OSS信息
export const client = new OSS({
region: "填写Bucket所在的区域,例如:oss-cn-guangzhou",
accessKeyId: "填写通过阿里云控制台创建的AccessKeyID",
accessKeySecret: "填写通过阿里云控制台创建的AccessKeySecret",
endpoint: "填写OSS访问域名",
bucket: "通过控制台或PutBucket创建的Bucket"
});
// 分片配置项
export const options = {
// 获取分片上传进度、断点和返回值。
progress: (p:any, cpt:any, res:any) => {
},
// 设置并发上传的分片数量。
parallel: 4,
// 设置分片大小。默认值为1 MB,最小值为100 KB。
partSize: 1024 * 1024,
// 设置请求头
headers: {
// 指定该Object被下载时的内容编码格式。
"Content-Encoding": "utf-8",
// 指定过期时间,单位为毫秒。
Expires: "1000",
// 指定Object的存储类型。
"x-oss-storage-class": "Standard",
// 指定Object标签,可同时设置多个标签。
"x-oss-tagging": "Tag1=1&Tag2=2",
// 指定初始化分片上传时是否覆盖同名Object。此处设置为true,表示禁止覆盖同名Object。
"x-oss-forbid-overwrite": "false",
},
// 指定上传文件类型
mime: "text/plain",
};