大文件上传demo,前端基于Uppy,后端基于koa

前言

文件上传基本上所有的管理系统之类的项目都有这么一个功能。因为使用了Element,可以方便的使用
其提供的Upload组件,对于普通上传来说基本上就够用了。但是有时候会涉及到大文件上传的需求,这时就会面临一些问题:比如文件上传超时。

自己做的话很麻烦,需要考虑到的东西非常多。这时可以考虑使用第三方封装的库。这里推荐Uppy ,主要是这个库一直在维护,有些库都是几年前的了,比如WebUploader

只是简单的研究,遇到问题,看官方文档

官方文档: https://uppy.io/docs/quick-start/

官方git: https://github.com/transloadit/uppy

准备工作

前端基于vue3,后端基于koa。(主业前端,后端是业余爱好只是简单了解)

前端

项目创建具体见:使用Vite搭建Vue3 + Ts项目,这里就不介绍了。

搭建完项目需要安装一下:axios element-plus ,运行项目后如下图:

大文件上传demo,前端基于Uppy,后端基于koa_第1张图片

后端

项目创建具体见:Koa学习1:初始化项目

运行项目后如下图:
大文件上传demo,前端基于Uppy,后端基于koa_第2张图片

整合

现在处理一下,让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,后端基于koa_第3张图片

简单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`);
});

大文件上传demo,前端基于Uppy,后端基于koa_第4张图片
大文件上传demo,前端基于Uppy,后端基于koa_第5张图片

大文件上传、断点续传

实现分片上传并且支持断点续传需要基于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}`);
});

执行效果

大文件上传demo,前端基于Uppy,后端基于koa_第6张图片
上传完成后会生成两个文件,如下:
第一个就是上传的文件,会变成一个二进制文件
第二个是这个文件的一下信息
大文件上传demo,前端基于Uppy,后端基于koa_第7张图片
前端Uppy库也会返回文件信息,如下图:

大文件上传demo,前端基于Uppy,后端基于koa_第8张图片

代码

代码放到码云上了,感兴趣的可以自己看一下

前端

地址
https://gitee.com/idonotyou/vue-upload

运行

npm i 
npm run dev

后端

地址
https://gitee.com/idonotyou/koa-upload

运行

npm i 
npm run dev

你可能感兴趣的:(Koa,学习,前端,大文件上传)