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

news2024/11/17 10:04:59

前言

文件上传基本上所有的管理系统之类的项目都有这么一个功能。因为使用了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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/972585.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

使用QPixmap显示图片

在QT中&#xff0c;经常需要我们显示图片&#xff08;作为背景&#xff0c;游戏元素&#xff0c;菜单背景&#xff0c;等待&#xff09;。 本文将用最简洁的方法介绍如何将图片素材显示在指定控件的指定位置。 一.基础知识 QPixmap 该类可以加载&#xff08;load&#xff0…

算法:合并两个有序数组---双指针[1]

1、题目&#xff1a; 给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2&#xff0c;另有两个整数 m 和 n &#xff0c;分别表示 nums1 和 nums2 中的元素数目。 请你 合并 nums2 到 nums1 中&#xff0c;使合并后的数组同样按 非递减顺序 排列。 注意&#xff1a;最终&a…

什么是websockret连接

什么是WebSocket WebSocket&#xff0c;是一种网络传输协议&#xff0c;位于 OSI 模型的应用层。可在单个 TCP 连接上进行全双工通信&#xff0c;能更好的节省服务器资源和带宽并达到实时通迅 客户端和服务器只需要完成一次握手&#xff0c;两者之间就可以创建持久性的连接&am…

一种结合白平衡统计信息和曝光信息的软光敏算法专利学习(专利三)

基础理论&#xff1a; 亮度计算&#xff1a; 对于白天模式而言&#xff0c;IR滤光片处于过滤红外光的状态&#xff0c;也就是说&#xff0c;摄像机的感光芯片所感受到的环境光中的红外光几乎为零&#xff1b;而对于夜晚模式而言&#xff0c;IR滤光片处于不过滤红外光的状态&am…

ABB 3BHB002916R0001 UFC721AE模拟输入卡

通道数目&#xff1a; UFC721AE 模拟输入卡通常具有多个输入通道&#xff0c;可以同时监测多个模拟信号。 输入类型&#xff1a; 这种卡片通常支持不同类型的模拟输入&#xff0c;例如电压信号、电流信号或其他传感器信号。 精度&#xff1a; UFC721AE 模拟输入卡通常具有高精…

树莓 LUMA-OLED.EXAMPLE使用

详细介绍在文件目录下的README.rst中 第一步 $ sudo usermod -a -G i2c,spi,gpio pi //好像没什么用 $ sudo apt install python3-dev python3-pip python3-numpy libfreetype6-dev libjpeg-dev build-essential //安装依赖包&#xff0c;树莓派中好像已经有了 $ sudo a…

Seata 笔记

Seata 笔记 分布式事务理论基础 CAP 定理 Consistency 一致性&#xff1a;用户访问分布式系统中的任意节点得到的结果都是一致的Availability 可用性&#xff1a;用户和访问任意健康节点都必须得到响应而不是超时拒绝Partition tolernance 分区容错性&#xff1a;出现独立分…

解决本地jar包导入maven

1、确定是否安装maven 2、输入导入命令 命令说明 <path-to-file>为你jar包所在的路径&#xff08;尽量简单并且不要含中文&#xff09; <group-id>为grouId号&#xff0c;与<artifact-id>组成唯一识别你jar包的坐标&#xff0c;当不在公共资源jar包中&#…

libbpf-bootstrap安卓aarch64适配交叉编译

1.为什么移植 疑惑 起初我也认为&#xff0c;像libbpf-bootstrap这样在ebpf程序开发中很常用的框架&#xff0c;理应支持不同架构的交叉编译。尤其是向内核态的ebpf程序本身就是直接通过clang的-target btf直接生成字节码&#xff0c;各个内核上的ebpf虚拟机大同小异&#xf…

万字解读 Android 车机核心 :CarService 的构成和链路~

前言 关于 Android 车机&#xff0c;之前分析过方控上自定义按键的输入机制和中控上旋钮输入的原理&#xff0c;但都局限于 Car Service 内 Input 相关模块。 一文了解 Android 车机如何处理中控的旋钮输入从实体按键看 Android 车载的自定义事件机制 本文将结合 Android 系…

LabVIEW开发感应电机在线匝间短路故障诊断系统

LabVIEW开发感应电机在线匝间短路故障诊断系统 工业中使用的超过85%的电动机是三相感应电动机。它们因其可靠性、设计便利性、高性能和过载能力而被广泛用于不同的应用&#xff0c;例如制造、加工、电力系统、运输等。无论它们的能力如何&#xff0c;它们都被认为是现代工业学…

Consider defining a bean of type问题解决

Consider defining a bean of type问题解决 Consider defining a bean of type问题解决 包之后&#xff0c;发现项目直接报错Consider defining a bean of type。 会有一些包你明明Autowired 但是还是找不到什么bean 导致你项目启动不了 解决方法一: 这个问题主要是因为项目拆包…

安卓 MeasureCache优化了什么?

安卓绘制原理概览_油炸板蓝根的博客-CSDN博客 搜了一下&#xff0c;全网居然没有人提过 measureCache。 在前文中提到过&#xff0c;measure的时候&#xff0c;如果命中了 measureCache&#xff0c;会跳过 onMeasure&#xff0c;同时会设置 PFLAG3_MEASURE_NEEDED_BEFORE_LAYOU…

【已解决】oracle获取最近2学年的数据

已解决 &#xff1a;oracle获取最近2学年的数据 SELECT * FROM (SELECT * FROM xx.JWXT_XSKB WHERE AND xn IN (‘2023-2024’,‘2022-2023’)); 问题 某某系统课表数据过大&#xff0c;要求只获取最近2学年的数据&#xff0c;不能写死。 思路 mysql 在子查询的WHERE子句中…

从0到1学会Git(第二部分):Git的本地操作和管理

写在前面:本文介绍了在本地仓库进行文件的处理以及本地的合并等操作。 前置知识:文件可以处在三个区域&#xff0c;分别为工作区&#xff0c;暂存区和本地仓库&#xff0c;我们此文的目标即是将文件存储在本地仓库中。我们可以将文件的区域理解为&#xff0c;cpu中&#xff0c…

苍穹外卖技术栈

重难点详解 1、定义全局异常 2、ThreadLocal ThreadLocal 并不是一个Thread&#xff0c;而是Thread的一个局部变量ThreadLocal 为每一个线程提供独立的存储空间&#xff0c;具有线程隔离的效果&#xff0c;只有在线程内才能取到值&#xff0c;线程外则不能访问 public void …

linux入门---动静态库的加载

目录标题 为什么会有动态库和静态库静态库的实现动态库的实现动静态库的加载 为什么会有动态库和静态库 我们来模拟一个场景&#xff0c;首先创建两个头文件 根据文件名便可以得知add.h头文件中存放的是加法函数的声明&#xff0c;sub.h头文件中存放的是减法函数的声明&#…

【每日运维】U盘启动盘安装 ESXi 6.7.0 安装卡在 loading /bnxtroce.v00

问题描述 ● ESXi 6.7.0 安装进度卡在loading /bnxtroce.v00 进度处 处理方法 ● 重新制作启动盘&#xff0c;写入方式改为&#xff1a;【USB-ZIPv2】 ● 设置服务器的 bios设置&#xff0c;启动方式改为【UEFI】 ● 重启开机安装即可

蛋白与蛋白互作预测 蛋白互作预测protein

How to prepare structures for HADDOCK? – Bonvin Labhttps://www.bonvinlab.org/software/bpg/structures/RosettaDock: 蛋白-蛋白复合物对接预测 - 知乎 (zhihu.com) 要进行LPR1-SEPP1复合物的结合亲和力预测&#xff0c;您可以按照以下步骤进行&#xff1a; 获取蛋白质结…

MySQL的Json类型个人用法详解

前言 虽然MySQL很早就添加了Json类型&#xff0c;但是在业务开发过程中还是很少设计带这种类型的表。少不代表没有&#xff0c;当真正要对Json类型进行特定查询&#xff0c;修改&#xff0c;插入和优化等操作时&#xff0c;却感觉一下子想不起那些函数怎么使用。比如把json里的…