前言
文件上传基本上所有的管理系统之类的项目都有这么一个功能。因为使用了Element
,可以方便的使用
其提供的Upload
组件,对于普通上传来说基本上就够用了。但是有时候会涉及到大文件上传的需求,这时就会面临一些问题:比如文件上传超时。
自己做的话很麻烦,需要考虑到的东西非常多。这时可以考虑使用第三方封装的库。这里推荐Uppy
,主要是这个库一直在维护,有些库都是几年前的了,比如WebUploader
。
只是简单的研究,遇到问题,看官方文档
官方文档: https://uppy.io/docs/quick-start/
官方git: https://github.com/transloadit/uppy
准备工作
前端基于vue3,后端基于koa
。(主业前端,后端是业余爱好只是简单了解)
前端
项目创建具体见:使用Vite搭建Vue3 + Ts项目,这里就不介绍了。
搭建完项目需要安装一下:axios
、element-plus
,运行项目后如下图:
后端
项目创建具体见:Koa学习1:初始化项目
运行项目后如下图:
整合
现在处理一下,让vue前端能够请求到数据
后端
需要安装koa2-cors
来解决跨域问题
npm install koa2-cors
修改后的main.js
// 导入Koa
const Koa = require("koa");
// 用于解决跨域
const cors = require("koa2-cors");
// 实例化
const app = new Koa();
app.use(cors());
// 中间件
app.use(async (ctx, next) => {
const start = Date.now();
await next();
ctx.body = "hellow koa";
});
// 监听端口
app.listen(5000, () => {
console.log(`app listening at http://localhost:5000`);
});
前端
修改vite.config.ts
配置文件
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
server: {
proxy: {
'/api': {
target: 'http://localhost:5000/',
changeOrigin: true,
rewrite: path => path.replace(/^\/api/, '')
}
}
}
});
修改App.vue
<template>
<div class="upload-container">
<el-button type="primary" ="getData">上传</el-button>
<p>数据是:{{ message }}</p>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import axios from 'axios';
const message = ref('');
// 请求数据
const getData = () => {
axios.get('http://localhost:5000/')
.then(res => {
message.value = res.data
console.log("数据是:", res.data)
})
}
</script>
<style scoped>
.upload-container {
display: flex;
justify-content: center;
align-items: center;
height: 700px;
}
</style>
效果图
简单demo,上传图片
前端
安装uppy
npm install /core /drag-drop /status-bar /xhr-upload
core
核心包drag-drop
用于实现拖拽上传status-bar
显示上传进度条xhr-upload
实现文件上传
<template>
<div class="upload-container">
<div id="drag-drop-area">
<!-- 默认样式,也可以在里面进行自定义 -->
</div>
<div id="status-bar"></div>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted } from "vue"
import { ElMessage } from 'element-plus'
import Uppy from '@uppy/core';
import DragDrop from '@uppy/drag-drop';
import StatusBar from '@uppy/status-bar';
import XHRUpload from '@uppy/xhr-upload';
//引入样式
import '@uppy/core/dist/style.min.css';
import '@uppy/drag-drop/dist/style.min.css';
// 1mb大小
const ONE_MB = 1024 * 1024;
const uppy = ref()
onMounted(() => {
uppy.value = new Uppy({
debug: true, // 允许拖拽
autoProceed: false, // 是否自动上传
restrictions: {
maxFileSize: 10 * ONE_MB, // 设置最大文件大小
maxNumberOfFiles: 5, // 设置最大上传文件数量
allowedFileTypes: ['.jpg', '.jpeg', '.png'] // 设置允许的文件类型
}
})
.use(DragDrop, { target: '#drag-drop-area', note: '拖放或点击' }) // 启用拖动
.use(StatusBar, { target: '#status-bar' }) //启用进度条
.use(XHRUpload, {
endpoint: 'http://localhost:5000/upload', // 设置上传文件的API接口
formData: true // 启用FormData发送数据
});
// 监听文件上传
uppy.value.on('upload-success', (file: any, response: any) => {
// console.log("上传的文件:", file)
console.log("返回的信息:", response)
if (response.body.code == 0) {
ElMessage.success(`文件${file.name}上传成功`)
} else {
ElMessage.error(`文件${file.name}上传失败,${response.body.message}`)
}
})
})
</script>
<style scoped>
.upload-container {
display: flex;
justify-content: center;
align-items: center;
height: 700px;
}
</style>
后端
安装koa-body
中间件,它可以方便地处理请求体中的文件数据。
npm install koa-body
安装koa-router
中间件,用于post
请求
npm install koa-router
修改main.js
// 导入Koa
const Koa = require("koa");
// 用于解决跨域
const cors = require("koa2-cors");
// 用于文件上传
const { koaBody } = require("koa-body");
// 用于处理路径
const path = require("path");
// 引入路由
const Router = require("koa-router");
// 注意如果有改动,则要重启一下。如果觉得麻烦可以设置热重启,具体见:https://blog.csdn.net/weixin_41897680/article/details/130907232
// 实例化
const app = new Koa();
const router = new Router();
app.use(cors());
// 配置文件上传
app.use(
koaBody({
multipart: true, // 允许多文件
formidable: {
uploadDir: path.join(__dirname, "uploads"), // 设置文件上传目录,必须有这个文件夹不然会报错
keepExtensions: true, // 保持文件扩展名
},
})
);
router.get("/", async (ctx) => {
ctx.body = "hello Koa";
});
// 文件上传
router.post("/upload", async (ctx) => {
// 获取上传的文件
try {
const file = await ctx.request.files.file;
console.log("文件信息:", file);
ctx.body = {
message: "文件上传成功",
data: {
size: file.size, //文件大小
fileName: file.originalFilename, // 文件的原始名称
filePath: file.filepath, // 在服务器上的保存路径
updateTime: file.lastModifiedDate, // 上次修改的时间
},
};
} catch (err) {
ctx.body = {
message: err,
data: {},
};
}
});
//挂载路由
app.use(router.routes()).use(router.allowedMethods());
// 监听端口
app.listen(5000, () => {
console.log(`app listening at http://localhost:5000`);
});
大文件上传、断点续传
实现分片上传并且支持断点续传需要基于Tus
Tus 是一种开放协议,用于基于 HTTP 构建的可恢复上传。这意味着 意外关闭选项卡或失去连接,让您继续,对于 实例,您的 10GB 上传,而不是重新开始。
Tus 支持任何语言、任何平台和任何网络。它需要一个客户端 和服务器集成工作。您可以签出客户端和服务器实现,以查找首选语言的服务器。
前端
前端变化不大,Uppy
为我们提供了对应的插件,修改后的代码如下:
<!-- 大文件上传 -->
<template>
<div class="upload-container">
<div id="drag-drop-area">
<!-- 默认样式,也可以在里面进行自定义 -->
</div>
<div id="status-bar"></div>
<br />
<el-button type="primary" ="pauseOrResume">{{ isUploadding ? '暂停' : '开始' }}</el-button>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted } from "vue"
import { ElMessage } from 'element-plus'
import Uppy from '@uppy/core';
import DragDrop from '@uppy/drag-drop';
import StatusBar from '@uppy/status-bar';
import Tus from '@uppy/tus';
//引入样式
import '@uppy/core/dist/style.min.css';
import '@uppy/drag-drop/dist/style.min.css';
// 1mb大小
const ONE_MB = 1024 * 1024;
// 是否正在上传,默认在上传
const isUploadding = ref(true)
let uppy: Uppy;
onMounted(() => {
uppy = new Uppy({
debug: true, // 允许拖拽
autoProceed: false, // 是否自动上传
restrictions: {
maxFileSize: 300 * ONE_MB, // 设置最大文件大小
maxNumberOfFiles: 5, // 设置最大上传文件数量
allowedFileTypes: ['.jpg', '.jpeg', '.png', '.zip'] // 设置允许的文件类型
},
})
.use(DragDrop, { target: '#drag-drop-area', note: '拖放或点击' }) // 启用拖动
.use(StatusBar, { target: '#status-bar' }) //启用进度条
.use(Tus, {
endpoint: 'http://127.0.0.1:5000/files', // 设置上传文件的API接口
limit: 5, // 限制同时进行的上传数量,默认值20,不要没有限制或者过大
chunkSize: 5 * ONE_MB // 设置分片的大小
});
// 监听文件上传
uppy.on('complete', (result: any) => {
// result是一个对象,属性是:
// 会返回failed(Array),因为可以多文件上传会返回一个数组
// successful(Array),因为可以多文件上传会返回一个数组,包含文件上传成功的信息
console.log("上传完成:",result)
if (Array.isArray(result.failed) && result.failed.length>0) {
ElMessage.error(`文件上传失败,${result.failed}`)
} else {
ElMessage.success(`文件上传成功`)
}
})
})
// 暂停与恢复
const pauseOrResume = () => {
if (isUploadding.value) {
// 正在上传
uppy.pauseAll()
} else {
// 暂停中
uppy.resumeAll()
}
isUploadding.value = !isUploadding.value
}
</script>
<style scoped>
.upload-container {
width: 300px;
margin: 100px auto;
height: 700px;
}
</style>
后端
后端变化挺大的,你需要将你的服务器变得支持Tus
,刚好官方提供了对应的插件(Java后台、php后台可以自行百度如何集成)
插件官方文档
https://github.com/tus/tus-node-server
官方集成案例,这个很重要,会介绍插件的属性、事件等
https://github.com/tus/tus-node-server/tree/main/packages/server
安装
npm i /file-store /server tus-node-server
代码
const Koa = require("koa");
const { Server } = require("@tus/server");
const { FileStore } = require("@tus/file-store");
// 用于解决跨域
const cors = require("koa2-cors");
const host = "127.0.0.1";
const port = 5000;
// 创建一个tusServer服务
const tusServer = new Server({
path: "/files", // 路由
datastore: new FileStore({ directory: "./files" }), // 文件存储的位置
});
const app = new Koa();
app.use(cors());
// 将 tus-server 添加为 Koa 的中间件
app.use(async (ctx, next) => {
// 注:tus-server 的处理程序要求精确匹配路由路径,这里无法使用koa-router。只能当作一个单独的中间件使用
await tusServer.handle.bind(tusServer)(ctx.req, ctx.res);
});
// 注:tus-server 的处理程序要求精确匹配路由路径,这里无法使用koa-router。只能当作一个单独的中间件使用
app.listen(port, host, () => {
console.log(`Server is running on http://${host}:${port}`);
});
执行效果
上传完成后会生成两个文件,如下:
第一个就是上传的文件,会变成一个二进制文件
第二个是这个文件的一下信息
前端Uppy
库也会返回文件信息,如下图:
代码
代码放到码云上了,感兴趣的可以自己看一下
前端
地址
https://gitee.com/idonotyou/vue-upload
运行
npm i
npm run dev
后端
地址
https://gitee.com/idonotyou/koa-upload
运行
npm i
npm run dev