• 大文件上传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 ,运行项目后如下图:

    在这里插入图片描述

    后端

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

    运行项目后如下图:
    在这里插入图片描述

    整合

    现在处理一下,让vue前端能够请求到数据

    后端
    需要安装koa2-cors来解决跨域问题

    npm install koa2-cors
    
    • 1

    修改后的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`);
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    前端

    修改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/, '')
                }
            }
        }
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    修改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>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    效果图
    在这里插入图片描述

    简单demo,上传图片

    前端

    安装uppy

    npm install /core /drag-drop /status-bar /xhr-upload
    
    • 1
    • 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>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67

    后端

    安装koa-body中间件,它可以方便地处理请求体中的文件数据。

    npm install koa-body
    
    • 1

    安装koa-router中间件,用于post请求

    npm install koa-router
    
    • 1

    修改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`);
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64

    在这里插入图片描述
    在这里插入图片描述

    大文件上传、断点续传

    实现分片上传并且支持断点续传需要基于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>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88

    后端

    后端变化挺大的,你需要将你的服务器变得支持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
    
    • 1

    代码

    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}`);
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    执行效果

    在这里插入图片描述
    上传完成后会生成两个文件,如下:
    第一个就是上传的文件,会变成一个二进制文件
    第二个是这个文件的一下信息
    在这里插入图片描述
    前端Uppy库也会返回文件信息,如下图:

    在这里插入图片描述

    代码

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

    前端

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

    运行

    npm i 
    npm run dev
    
    • 1
    • 2

    后端

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

    运行

    npm i 
    npm run dev
    
    • 1
    • 2
  • 相关阅读:
    测试用例设计方法之等价类划分方法
    【Qt炫酷动画】专栏导航目录
    【Java基础】继承、抽象类、注解
    Jmeter接口测试:jmeter导入和导出接口的处理
    Nginx 如何配置http server 、负载均衡(反向代理)
    基于51单片机DS18B20温度及电流检测-proteus仿真-源程序
    聊聊 Redis 是如何进行请求处理
    Jetpack Compose学习(11)——Navigation页面导航的使用
    1.1操作系统课程介绍
    五、深度学习优化算法
  • 原文地址:https://blog.csdn.net/weixin_41897680/article/details/132617289