vue+node后台处理大文件切片上传--前端部分

news2024/9/9 6:17:29

本文主要介绍,在vue3+vite项目下,如何进行有效的大文件上传,本文章主要讲大文件切片上传方式,并提供简单的demo代码供参考

首先,请确保已经创建好项目,这一步跳过。
1、为了选择合适的文件,我们可以先写一个组件,用来选择文件并且将所选择的文件暴露到app.vue中。
下面是我自己写的组件,可以根据自己的需要进行修改操作。主要的部分有,将默认的拖拽事件,比如进入,悬浮,离开全部阻止默认行为,只允许放置操作,并且在放置操作完成文件内容的读取,代码如下:
FileUpload.vue

<template>
    <div>
        <div class="upload-demo" ref="up">
            <el-icon class="el-icon--upload"><upload-filled /></el-icon>
        </div>
    </div>
</template>

<script setup name="FileUpload">
import { UploadFilled } from '@element-plus/icons-vue'
import { onMounted, reactive, ref } from 'vue'

const up = ref(null);
const fileUrl = reactive({});
const emit = defineEmits(['getFile']);
onMounted(() => {
    console.log(up.value)
    up.value.addEventListener('dragenter', (e)=>{ //阻止默认事件
        e.preventDefault();
        e.stopPropagation();
    })
    up.value.addEventListener('dragover', (e)=>{
        e.preventDefault();
        e.stopPropagation();
    })
    up.value.addEventListener('dragleave', (e)=>{
        e.preventDefault();
        e.stopPropagation();
    })
    up.value.addEventListener('drop', async (e)=>{
        e.preventDefault();
        e.stopPropagation();
        const f = e.dataTransfer.files;  //在此处获取用户拖拽进去组件的文件数组
        const file = f[0];   //取第一个,在这里我默认只拖拽一个,可以自己选择性进行扩展,如果怕f为空报错,选择trycatch,我为了方便就没写
        if(!file) return console.error('没有文件')
        if(file.size > 1024*1024*1024*2) return console.error('文件太大'); //文件超过2G,太大不上传
        if(!file.type.startsWith('video')) return console.error('只能上传视频文件'); 
        Object.assign(fileUrl, file) 
        emit('getFile', file);
    })
})
defineExpose({fileUrl})
</script>

<style scoped>
.upload-demo{
    width: 200px;
    height: 100px;
    border: 1px dotted gray;
    transition: all 3s;
    font-size: 100px;
    display: flex;
    justify-content: center;
    align-items: center;
}
.upload-demo:hover{
    border: 1px dotted blue
}
</style>

在app.vue中引用组件:

<FileUpload @getFile="getFile"></FileUpload>

其中涉及到的getfile方法,为子组件调用函数触发的方法,主要用来传递选中的文件:

let url = ref('');
let upfile = reactive<{[key: string]: string}>({});
let appfile:any = null;
function getFile(file:any){
  console.log('child set:', file);
  console.log(typeof file);  //File类型,根据原型链,底层是object
  if(typeof file != 'object'){
    return console.error('文件格式不对,非对象');
  }
  url.value = URL.createObjectURL(file);  //创建临时URL地址,索引到用户本地的文件地址,方便页面展示
  appfile = file;
  console.log(appfile);
  Object.assign(upfile, {  //创建一个包含文件地址,文件名,类型的对象进行保存
    url,
    type: file.type,
    name: file.name
  })
}

子组件到这里就结束,我将主体逻辑写到app中,方便展示,最好的方式还是全部封装到子组件,达到高内聚低耦合,每一个模块只干一件事。
2、获取唯一的文件名称
通常,对于每一个相同的文件,我们需要为其生成一个唯一的名字,然后上传到服务器,根据唯一名称,判断是否同一个文件,节省资源的同时,提高用户体验。如何根据文件内容获取唯一的文件名称,我们先将文件内容转为二进制,再利用浏览器提高的api:crypto.subtle.digest生成哈希值(注意,该方法异步,容易被忽略),再转为16进制字符串,这就是文件的唯一名:

async function getFilename(){
  const hashname = await getFileHashname();
  const fileExtension = upfile.name.split('.').pop();
  return `${hashname}.${fileExtension}`;
}
async function getFileHashname(){
  console.log(appfile);
  const arrayBuffer = await appfile.arrayBuffer();
  const hashBuffer = await crypto.subtle.digest('SHA-256', arrayBuffer);
  const hex = Array.from(new Uint8Array(hashBuffer)).map(b => b.toString(16).padStart(2, '0')).join('');
  return hex;
}

3、切片操作
首先认识一下文件大小的概念,浓缩为以下公式:
1GB = 1024MB = 1024*1024KB = 1024*1024*1024B
那么文件切片,也就是将很大的文件切成很多块,每块的大小我们自己决定,当然,在现实应用中,是需要根据网络流量,服务器的负载等因素进行动态调整这个切片的大小,我们切片的过程中,完全可以把File当成一个具有一定大小的数组,如果你这么思考,那么下面的代码将通俗易懂,主要做的就是存储切后的块,此外,我们还需要将切片的顺序写入切片名字中,后端可以识别然后进行拼接。

//切片操作 切成10*1024*1024,也就是10MB
const CHUNKS_SIZE = 10 * 1024 * 1024;
async function sectionFile(filename:string){
  console.log(filename);
  const chunks = createChunks(filename);
  console.log(chunks);
  return chunks;
}
function createChunks(filename: string){
  let chunks = [];
  const count = Math.ceil(appfile.size / CHUNKS_SIZE);
  for(let i = 0; i < count; i++){
    let chunk = appfile.slice(i*CHUNKS_SIZE, (i+1)*CHUNKS_SIZE);
    chunks.push({
      chunk,
      chunkFileName: `${filename}-${i}`
    })
  }
  return chunks;
} 

3、封装axios
这一步主要做的操作是提前封装一下axios,更加好的适应后端返回的主体内容,当然,你可以选择不进行这一步,因为这不是必须的。
简单封装axios,判断是否返回的主体内容有success,否则抛出错误。

import axios from 'axios';


const axiosInstance = axios.create({
  baseURL: 'http://localhost:8080'
})
axiosInstance.interceptors.response.use(
  (response)=>{ 
    if(response.data && response.data.success){
      return response.data;
    } else {
      return new Error(response.data.msssage || 'service error')
    }
  },
  (error)=>{
    console.log(error);
    return error;
})

4、上传进度条
一开始小伙伴肯定很好奇,为什么我发送的请求看不见进度,但是别人怎么看见进度的,难道后端有返回参数嘛,当然不是!如果要一直返回参数,那这个开销可就不得了啦! 其实事情是这样子的,axios请求提供一个方法,来监听上传的进度,也就是请求的进度,如果你能够熟练使用这个方法,那么对页面的一些交互大有裨益!这个方法就是onUploadProgress! 其中参数是progressEvent,有两个重要属性,total和loaded,意义如同属性名:但是值得注意的是:loaded是一个0~1的范围数据,所以下面的代码有细节,请注意哦,如果你发现这个小小小的细节,请在评论区打出!~

let progress = reactive<{[key: string]: number}>({});
onUploadProgress: (progressEvent)=>{
          if(!progressEvent.total) return ;
          const percentProgress = Math.round(progressEvent.loaded*100 / progressEvent.total)
          Object.assign(progress, {
            ...progress, 
            [item.chunkFileName]: percentProgress
          })
          console.log("shangchuangzhong");
      }

5、上传文件
到这里,就差最重要的上传文件啦~先来一个按钮,:

 <button @click="uploadFile">上传</button>
 <el-progress v-for="(item, index) in getpro" :percentage="item" :key="index"/>

所谓文件切片,其实最重要的就是实现文件切片后,可以将切片并行上传,可以极大加快上传的效率,因为充分利用了服务器的吞吐量和效率。所以上述我们已经完成切片,而且将切片的顺序写入切片名字中,后端可以识别然后进行拼接,所以在上传完切片后,还需要发送一个合并文件的请求:

async function uploadFile(){
  const filename = await getFilename();
  console.log('name: ', filename);
  const chunks = await sectionFile(filename);
  const requsetList = chunks.map(item => {
    return axiosInstance.post(`/upload/${filename}`, item.chunk, {
      headers:{
        'Content-Type':'application/octet-stream'
      },
      params: {
        chunkFileName: item.chunkFileName
      },
      onUploadProgress: (progressEvent)=>{
          if(!progressEvent.total) return ;
          const percentProgress = Math.round(progressEvent.loaded*100 / progressEvent.total)
          Object.assign(progress, {
            ...progress, 
            [item.chunkFileName]: percentProgress
          })
          console.log("shangchuangzhong");
      }
    })
  })
  try {
    await Promise.all(requsetList);
    //切片发送完还需要发一个合并文件内容的请求
    await axiosInstance.get(`/mergeFile/${filename}`, {
      headers: {
        'Content-Type': 'application/json'
      }
    }).then(()=>{
      console.log('rollback');
      // Object.assign(progress, null) //没法做到清空对象
      Object.keys(progress).forEach(key => {
        delete progress[key];
      });
    })
    console.log('上传完毕!')
  } catch (error) {
    console.log(error);
    return ;
  }
}

到这里就ok啦!

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

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

相关文章

【Python】 ValueError: too many values to unpack 解决方案

【Python】 ValueError: too many values to unpack 解决方案 在Python编程中&#xff0c;ValueError: too many values to unpack是一个常见的错误&#xff0c;通常出现在使用解包操作时。本文将深入探讨这个错误的原因、解决思路、解决方法&#xff0c;并通过具体案例帮助大…

【Python学习手册(第四版)】学习笔记09.3-Python对象类型-分类、引用VS拷贝VS深拷贝、比较、相等、真假值等详解

个人总结难免疏漏&#xff0c;请多包涵。更多内容请查看原文。本文以及学习笔记系列仅用于个人学习、研究交流。 这部分稍杂&#xff0c;视需要选择目录读取。 主要讲的是对之前的所有对象类型作复习&#xff0c;以通俗易懂、由浅入深的方式进行介绍&#xff0c;所有对象类型…

『康之泉活水馆』手游:打造夏日梦幻水世界

设计背景 夏日的热浪与城市的喧嚣困扰着忙碌奔波的人群&#xff0c;康之泉活水馆&#xff0c;作为多功能的室内水上乐园&#xff0c;以其独特的魅力&#xff0c;成为夏日避暑的理想之地&#xff0c;让身心得以彻底放松。 设计理念 优联前端以康之泉品牌IP形象“康康”为灵感&a…

[GYCTF2020]FlaskApp (pin码,jinja2绕过注入)

题目就是flask 下面是判断模版注入的方法 a{*comment*}b和{{7*7}}base64编码后解码都报错no&#xff0c;无法判断模版引擎 直接用下jinja2的试一试&#xff0c;把编码后的密文拿去解码&#xff0c;payload&#xff1a; {{"".__class__mro(2)__subclasses__()}} 报…

英文文献翻译方法哪个好?高效率的翻译方法分享

三伏天的酷热也抵挡不住学术人探索知识的脚步&#xff0c;阅读和翻译英文文献几乎已经成为了许多研究者和学者的日常。然而在面对浩如烟海的英文资料时&#xff0c;如何高效准确地进行翻译&#xff0c;成为了亟待解决的问题。 今天我便挖掘到了5款实用的英文文献翻译工具&…

4.1.1、操作系统的概述

操作系统的作用:通过资源管理提高计算机系统的效率;改善人机界面向用户提供友好的工作环境。 操作系统的特征:并发性、共享性、虚拟性、不确定性。 操作系统的功能:进程管理、存储管理、文件管理、设备管理、作业管理。 操作系统的分类:批处理操作系统、分时操作系统(轮流使…

美股:苹果选择谷歌芯片支持人工智能技术

最近的研究报告显示&#xff0c;苹果公司在其新一代人工智能工具和功能套件中选择依赖谷歌设计的芯片&#xff0c;而非市场领导者 Nvidia。这一决定引发了业界的关注&#xff0c;尤其是考虑到Nvidia在人工智能处理器市场的主导地位。 谷歌云的TPU在苹果的AI基础设施中发挥关键作…

计算机再过几年会没落?

大部分人卷的计算机&#xff1a;Java web 实际上的计算机&#xff1a;web&#xff0c;图形学&#xff0c;Linux系统开发&#xff0c;一系列嵌入式开发&#xff0c;数据库&#xff0c;高性能服务器&#xff0c;中间件开发&#xff0c;三维建模&#xff0c;网络安全&#xff0c;…

vue2 封装弹框组件

安装 element-ui npm install element-ui --save ---force main.js 导入 import Vue from vue; import ElementUI from element-ui; import element-ui/lib/theme-chalk/index.css; import App from ./App.vue; Vue.use(ElementUI); new Vue({ el: #app, render: h > h(Ap…

Minio、MySQL、Redis、Milvus 安装

CPU&#xff1a;2核↑&#xff0c;内存&#xff1a;4GB↑ 开发工具&#xff1a;eclipse-jee、MySQL Workbench、MobaXterm、Redis Insight... 操作系统&#xff1a;CentOS Stream 9&#xff08;生产环境&#xff09;、Windos 11 Ubuntu 22.04.3&#xff08;开发环境&#xf…

使用 Kibana 和 Vega 构建高级可视化

作者&#xff1a;来自 Carly Richmond 为了解释在 Kibana 中构建 Vega 可视化的基础知识&#xff0c;我将使用此 GitHub 存储库中的 2 个示例。具体来说&#xff0c;我将介绍&#xff1a; 使用 Elasticsearch 聚合进行数据采购轴和标记事件和信号&#xff08;例如工具提示和更…

工具使用备忘录

npm npm是node包管理工具。yarn是npm的替代品&#xff0c;看起来使用更加广泛。cnpm是中国镜像。 cnpm安装时&#xff0c;曾经出现过错误。不进行安全验证后运行成功。 目前在WSL上可以全局使用cnpm 当时解决的方案是将报错信息copy下来&#xff0c;直接看通义千问的结果。 …

时间序列中多维度、多变量、多元、多尺度

目录 多尺度 多维度 多变量 多元 区别 举例&#xff1a; 多尺度 多尺度时间序列分析是指在不同的时间尺度上对数据进行分析。例如&#xff0c;某些现象可能在短期内表现出一种模式&#xff0c;而在长期内表现出另一种模式。多尺度分析可以帮助我们捕捉这些不同时间尺度上…

北斗RTK高精度定位系统介绍

北斗RTK高精度定位系统是一种高精度、高可靠、高效率的定位系统。它采用北斗卫星导航系统&#xff0c;结合实时动态差分RTK技术&#xff0c;能够实现亚米级的定位准确度&#xff0c;适用于石油化工、工厂、工地、园区、环卫等领域。 北斗RTK的核心技术是RTK技术&#xff0c;即实…

C++ | string

前言 本篇博客讲解c中的string类的使用(常用接口) &#x1f493; 个人主页&#xff1a;普通young man-CSDN博客 ⏩ 文章专栏&#xff1a;C_普通young man的博客-CSDN博客 ⏩ 本人giee:普通小青年 (pu-tong-young-man) - Gitee.com 若有问题 评论区见&#x1f4dd; &#x1f389…

在 Elasticsearch 中实现采集自动扩展

作者&#xff1a;来自 Elastic Pooya Salehi, Henning Andersen, Francisco Fernndez Castao 正确调整 Elasticsearch 集群的大小并不容易。集群的最佳大小取决于集群正在经历的工作负载&#xff0c;而工作负载可能会随着时间的推移而变化。自动扩展会自动调整集群大小以适应工…

【JavaScript】详解JavaScript语法

文章目录 一、变量和数据类型二、运算符三、条件语句四、循环语句五、函数六、对象和数组七、ES6新特性八、实际应用案例 JavaScript是一门广泛应用于Web开发的编程语言。掌握JavaScript语法是成为前端开发者的第一步。本文将详细介绍JavaScript的基本语法&#xff0c;包括变量…

“Assistants“ has no attribute “files“ in openAI assistants

题意&#xff1a;在 OpenAI 的助手&#xff08;assistants&#xff09;中&#xff0c;“Assistants” 没有 “files” 这个属性。 问题背景&#xff1a; assistant_file client.beta.assistants.files.create(assistant_id st.session_state.assistant_id,file_id st.sessi…

2024年最佳骨传导耳机推荐:五款不容错过的选择!

作为音乐爱好者的我&#xff0c;也一直在寻找一款好的骨传导耳机&#xff0c;听音乐对我来说不仅仅是一种消遣方式&#xff0c;更多是一种对生活、工作上压力和困难的舒缓&#xff0c;所以今天给大家推荐几款骨传导耳机。今天推荐这几款骨传导耳机都是比较有性价比&#xff0c;…

Oracle VM VirtualBox 异常退出,如何解决??

&#x1f3c6;本文收录于《CSDN问答解惑-专业版》专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收…