foreword
File upload basically all management systems and other projects have such a function. Because it is used Element
, it is convenient to use the components
provided by it Upload
, which is basically enough for ordinary uploading. But sometimes it involves the need to upload large files, and then you will face some problems: such as file upload timeout.
It is very troublesome to do it yourself, and there are so many things to consider. At this time, you can consider using a third-party packaged library. It is recommended here Uppy
, mainly because this library has been maintained, and some libraries are a few years ago, for example WebUploader
.
Just simple research, encounter problems, see official documents
Official documentation: https://uppy.io/docs/quick-start/
Official git: https://github.com/transloadit/uppy
Preparation
The front end is based on vue3, and the back end is based on koa
. (The front end is the main business, the back end is a hobby, just a simple understanding)
front end
For details on project creation, see: Use Vite to build a Vue3 + Ts project , which will not be introduced here.
After building the project, you need to install: axios
, element-plus
, and run the project as shown below:
rear end
For details on project creation, see: Koa Learning 1: Initializing Projects
After running the project, as shown below:
to integrate
Let's deal with it now, so that the vue front end can request data
The backend
needs to be installed koa2-cors
to solve cross-domain problems
npm install koa2-cors
after editedmain.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`);
});
front end
Modify vite.config.ts
the configuration file
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/, '')
}
}
}
});
ReviseApp.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>
renderings
Simple demo, upload pictures
front end
install uppy
npm install /core /drag-drop /status-bar /xhr-upload
core
core packagedrag-drop
Used to implement drag and drop uploadstatus-bar
Show upload progress barxhr-upload
Implement file 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>
rear end
Install koa-body
middleware, which can conveniently handle file data in the request body.
npm install koa-body
Install koa-router
middleware for post
requests
npm install koa-router
Revisemain.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`);
});
Large file upload, resumable upload
To implement multipart uploads and support resuming uploads from breakpoints needs to be based onTus
Tus is an open protocol for resumable uploads built on top of HTTP. This means accidentally closing a tab or losing your connection, allowing you to continue, for instance, your 10GB upload instead of starting over.
Tus supports any language, any platform, and any network. It requires a client and server integration to work. You can check out the client and server implementations to find a server for your preferred language.
front end
The front end has not changed much, and Uppy
the corresponding plug-in is provided for us. The modified code is as follows:
<!-- 大文件上传 -->
<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>
rear end
The backend has changed a lot. You need to make your server support it Tus
. It just so happens that the official provides corresponding plug-ins (Java backend and php backend can be integrated by Baidu)
Plugin official documentation
https://github.com/tus/tus-node-server
The official integration case, this is very important, will introduce the properties and events of the plug-in
https://github.com/tus/tus-node-server/tree/main/packages/server
Install
npm i /file-store /server tus-node-server
the code
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}`);
});
Execution effect
After the upload is complete, two files will be generated, as follows:
the first is the uploaded file, which will become a binary file,
and the second is the information about the file .
The front-end Uppy
library will also return the file information, as shown in the figure below:
the code
The code is put on the code cloud, if you are interested, you can take a look for yourself
front end
Address
https://gitee.com/idonotyou/vue-upload
run
npm i
npm run dev
rear end
Address
https://gitee.com/idonotyou/koa-upload
run
npm i
npm run dev