大文件上傳源碼,支持單個大文件與多個大文件

大文件上傳源碼,支持單個大文件與多個大文件

  • Ⅰ 思路
  • Ⅱ 具體代碼
    • 前端--單個大文件
    • 前端--多個大文件
    • 前端接口
    • 后端

Ⅰ 思路

具體思路請參考我之前的文章,這里分享的是上傳流程與源碼
https://blog.csdn.net/sugerfle/article/details/130829022

在這里插入圖片描述

Ⅱ 具體代碼

前端–單個大文件

<template><el-upload ref="fUploadFile"multipleaction="":show-file-list="false":http-request="fhandleFileUpload":limit="2":on-exceed="fhandleExceed":on-success="fhandleSuccess":on-change="fhandleChange":file-list="fFileList"><el-button class="button" type="primary">上傳</el-button></el-upload>
</template><script>
import SparkMD5 from "spark-md5";
import {fcheckFileExists, fgetUploadedChunks, fMergeChunks, fUploadedChunks} from "@/api/record/recordfile";
export default {name: "VCUpload",props:['parentId'],data(){return{fFileList: [], // 文件列表fFile: null, // 當前文件fChunkSize: 3 * 1024 * 1024, // 分片大小:3MBfChunks: [], // 分片數組fUploadedChunks: [], // 已上傳分片索引fFileType:"",fFileName:""}},methods:{fhandleChange(file){this.file = file.raw;var fileName = file.name;var fileArr = fileName.split('.');this.fFileType=fileArr[fileArr.length-1];this.fFileName = file.nameconsole.log("fhandleChange",file.raw)this.initChunks();},fhandleFileUpload(data){console.log("Flx組件",data)},fhandleExceed(){},fhandleSuccess(){this.$refs.fUploadFile.clearFiles();},//  =========  分片相關業務方法   =============// 初始化分片initChunks() {if (!this.file) return;const file = this.file;this.fChunks = [];let cur = 0;while (cur < file.size) {this.fChunks.push({index: this.fChunks.length,file: file.slice(cur, cur + this.fChunkSize),});cur += this.fChunkSize;}console.log("初始化分片",this.fChunks)this.calculateMD5(); // 計算文件 MD5},// 計算文件 MD5calculateMD5() {const spark = new SparkMD5.ArrayBuffer();const reader = new FileReader();reader.readAsArrayBuffer(this.file);reader.onload = (e) => {spark.append(e.target.result);const md5 = spark.end();this.checkFileExists(md5); // 檢查文件是否已存在};},// 檢查文件是否已存在(秒傳邏輯)checkFileExists(md5) {console.log("秒傳邏輯",md5)const data = {fileMd5:md5,parentId:this.parentId}fcheckFileExists(data).then(resp=>{console.log("檢查文件是否已存在",resp)if(resp.data.data){this.$message.success('文件上傳成功!');}else {this.getUploadedChunks(md5); // 獲取已上傳分片}}).catch(res=>{this.$message.success('文件檢查失敗!');})},// 獲取已上傳分片(斷點續傳邏輯)getUploadedChunks(md5) {const data = {md5: md5}fgetUploadedChunks(data).then(resp=>{console.log("獲取數據庫中已經上傳分片",resp.data.data)this.fUploadedChunks = resp.data.data;// 開始上傳this.startUpload(md5);})},// 開始上傳async startUpload(md5) {const allChunkLength =  this.fChunks.length;for(let i=0;i<allChunkLength;i++){console.log("是否需要繼續上傳",this.fUploadedChunks.includes(this.fChunks[i].index+""))if (!this.fUploadedChunks.includes(this.fChunks[i].index+"")) {const formData = new FormData();let formDataObj = {chunkIndex:this.fChunks[i].index,md5:md5}let sendData = JSON.stringify(formDataObj)formData.append('dto',new Blob([sendData],{type:"application/json"}))formData.append('chunkFile',this.fChunks[i].file)const result = await this.fetchUploadedChunks(formData);if(result=="error"){this.$message.success('文件分片上傳失敗!');return}console.log("上傳分片成功",result)this.fUploadedChunks.push(this.fChunks[i].index);// 記錄已上傳分片if (this.fUploadedChunks.length === this.fChunks.length) {// 合并分片console.log("合并分片")this.mergeChunks(md5);}}}},fetchUploadedChunks(formData){return new Promise((resolve,reject)=>{fUploadedChunks(formData).then((resp)=>{resolve(resp.data.data)}).catch(err=>{reject("error")})})},mergeChunks(md5) {const data = {md5: md5,fileType:this.fFileType,parentId:this.parentId,name: this.fFileName,}fMergeChunks(data).then(resp=>{console.log("分片合并成功",resp.data.data)this.$message.success('文件上傳完成!');this.fFileList = []})},}
}
</script><style scoped lang="scss"></style>

前端–多個大文件

<template><div style="display: flex"><el-upload ref="fUploadFile"action="":auto-upload="false":show-file-list="false":multiple="true":http-request="fhandleFileUpload":limit="5":on-exceed="fhandleExceed":on-success="fhandleSuccess":file-list="fFileList"><el-button class="button" type="primary">選擇文件</el-button></el-upload><el-button class="button" type="primary" @click.native="fsubmitUpload">上傳</el-button></div>
</template><script>
import SparkMD5 from "spark-md5";
import {fcheckFileExists, fgetUploadedChunks, fMergeChunks, fUploadedChunks} from "@/api/record/recordfile";
export default {name: "VCUpload",props:['parentId'],data(){return{fFileList: [], // 文件列表fChunkSize: 3 * 1024 * 1024, // 分片大小:3MB}},methods:{// 自定義上傳方法fhandleFileUpload(options){console.log("自定義上傳方法",options)const { file } = options;const fileName = file.name;const fileArr = fileName.split('.');const fFileType=fileArr[fileArr.length-1];// 初始化分片const fChunks =  this.initChunks(file);// 計算文件 MD5 并綁定到文件對象this.calculateMD5(file).then((md5) => {file.md5 = md5; // 將 MD5 值綁定到文件對象// 檢查文件是否已存在(秒傳邏輯)this.checkFileExists(md5).then((exists) => {if (exists) {this.$message.success(`${file.name} 已存在,無需上傳!`);return;}// 獲取已上傳分片(斷點續傳邏輯)this.getUploadedChunks(md5).then(res=>{const fUploadedChunks = res;// 開始上傳this.startUpload(md5,fChunks,fUploadedChunks,fileName,fFileType);});});});},// 文件超出限制時的回調fhandleExceed(files, fileList) {this.$message.warning(`最多只能上傳5個文件!`);},fsubmitUpload(){this.$refs.fUploadFile.submit();},fhandleSuccess(){this.$refs.fUploadFile.clearFiles();},//  =========  分片相關業務方法   =============// 初始化分片initChunks(fArgsfile) {const file = fArgsfile;const fChunks = [];let cur = 0;while (cur < file.size) {fChunks.push({index: fChunks.length+"",file: file.slice(cur, cur + this.fChunkSize),});cur += this.fChunkSize;}console.log("初始化分片",fChunks)return fChunks;},// 計算文件 MD5calculateMD5(fArgsfile) {// 計算文件 MD5return new Promise((resolve) => {const spark = new SparkMD5.ArrayBuffer();const reader = new FileReader();reader.readAsArrayBuffer(fArgsfile);reader.onload = (e) => {spark.append(e.target.result);resolve(spark.end());};});},// 檢查文件是否已存在(秒傳邏輯)checkFileExists(md5) {return new Promise((resolve) => {const data = {fileMd5:md5,parentId:this.parentId}fcheckFileExists(data).then(resp=>{console.log("檢查文件是否已存在",resp)resolve(resp.data.data)})})},// 獲取已上傳分片(斷點續傳邏輯)getUploadedChunks(md5) {return new Promise((resolve) => {const data = {md5: md5}fgetUploadedChunks(data).then(resp=>{console.log("獲取數據庫中已經上傳分片",resp.data.data)resolve(resp.data.data)})})},// 開始上傳async startUpload(md5,fChunks,fUploadedChunks,fileName,fFileType) {const allChunkLength =  fChunks.length;for(let i=0;i<allChunkLength;i++){console.log("是否需要繼續上傳",fUploadedChunks,fChunks[i].index)if (!fUploadedChunks.includes(fChunks[i].index)) {const formData = new FormData();let formDataObj = {chunkIndex:fChunks[i].index,md5:md5}let sendData = JSON.stringify(formDataObj)formData.append('dto',new Blob([sendData],{type:"application/json"}))formData.append('chunkFile',fChunks[i].file)const result = await this.fetchUploadedChunks(formData);if(result=="error"){this.$message.success('文件分片上傳失敗!');return}console.log("上傳分片成功",result)fUploadedChunks.push(fChunks[i].index);// 記錄已上傳分片if (fUploadedChunks.length === fChunks.length) {// 合并分片this.mergeChunks(md5,fileName,fFileType);}}}},fetchUploadedChunks(formData){return new Promise((resolve,reject)=>{fUploadedChunks(formData).then((resp)=>{resolve(resp.data.data)}).catch(err=>{reject("error")})})},mergeChunks(md5,fileName,fFileType) {const data = {md5: md5,fileType:fFileType,parentId:this.parentId,name: fileName,}fMergeChunks(data).then(resp=>{console.log("分片合并成功",resp.data.data)this.$message.success(fileName+'文件上傳完成!');})},}
}
</script><style scoped lang="scss"></style>

前端接口

export function fcheckFileExists(obj) {return request({url: '/admin/recordfile/check-file',method: 'post',data: obj})
}export function fgetUploadedChunks(obj) {return request({url: '/admin/recordfile/get-uploaded-chunks',method: 'post',data: obj})
}export function fUploadedChunks(obj) {return request({url: '/admin/recordfile/upload-chunk',method: 'post',data: obj})
}export function fMergeChunks(obj) {return request({url: '/admin/recordfile/merge-chunks',method: 'post',data: obj})
}

后端

在這里插入圖片描述

//=============  分片上傳  ================@PostMapping("/check-file")public R<Boolean> checkFileExists(@RequestBody Map<String, String> request) {String fileMd5 = request.get("fileMd5");String parentId = request.get("parentId");QueryWrapper<RecordFile> q = new QueryWrapper<>();q.eq("md5",fileMd5);RecordFile recordFile = recordFileService.getOne(q);if (recordFile != null && Objects.equals(recordFile.getUploadStatus(), "1")) {// 文件已存在且已完成上傳if(Long.parseLong(parentId)!=recordFile.getParentId()){RecordFile recordFile1 = new RecordFile();recordFile1.setName(recordFile.getName());recordFile1.setParentId(Long.parseLong(parentId));recordFile1.setPath(recordFile.getPath());recordFile1.setType("file");recordFile1.setFileType("upload");recordFile1.setUploadStatus("1");recordFileService.save(recordFile1);}return R.ok(Boolean.TRUE);}// 如果文件不存在,則創建新的文件記錄,并設置初始狀態為未完成if (recordFile == null) {recordFile = new RecordFile();recordFile.setMd5(fileMd5);recordFile.setUploadStatus("0"); // 設置初始狀態為未完成recordFileService.save(recordFile);}// 文件不存在或未完成上傳return  R.ok(Boolean.FALSE);}@PostMapping("/get-uploaded-chunks")public R<List<String>> getUploadedChunks(@RequestBody Map<String, String> request) {String md5 = request.get("md5");QueryWrapper<RecordFileChunk> q = new QueryWrapper<>();q.eq("md5",md5);List<RecordFileChunk> chunks = recordFileChunkService.list(q);List<String> uploadedChunkIndexes = chunks.stream().filter(chunk -> Objects.equals(chunk.getIsUploaded(), "1")).map(RecordFileChunk::getChunkIndex).collect(Collectors.toList());return R.ok(uploadedChunkIndexes);}@PostMapping("/upload-chunk")public R uploadChunk(@RequestPart("dto") RecordFileChunk recordFileChunk, @RequestPart(name = "chunkFile")MultipartFile file) {if(file!=null){R r = sysFileService.uploadFile222(file);RecordFileChunk myRecordFileChunk = new RecordFileChunk();myRecordFileChunk.setChunkPath((((Map<String, String>)r.getData()).get("url")));myRecordFileChunk.setChunkSize(file.getSize());myRecordFileChunk.setChunkIndex(recordFileChunk.getChunkIndex());myRecordFileChunk.setMd5(recordFileChunk.getMd5());myRecordFileChunk.setIsUploaded("1");R.ok(recordFileChunkService.save(myRecordFileChunk));return R.ok(Boolean.TRUE);}else {return R.ok(Boolean.FALSE);}}@PostMapping("/merge-chunks")public R mergeChunk(@RequestBody Map<String, String> request) {String md5 = request.get("md5");String fileType = request.get("fileType");String parentId = request.get("parentId");String myName = request.get("name");QueryWrapper<RecordFileChunk> q = new QueryWrapper<>();q.eq("md5",md5);List<RecordFileChunk> chunks = recordFileChunkService.list(q);List<RecordFileChunk> sortchunks = chunks.stream().sorted(Comparator.comparingInt(o -> Integer.parseInt(o.getChunkIndex()))).collect(Collectors.toList());String fileName = IdUtil.simpleUUID() + StrUtil.DOT +  fileType;String filePath = String.format("/admin/sys-file/%s/%s", properties.getBucketName(), fileName);String var10000 = this.properties.getLocal().getBasePath();String dir = var10000 + FileUtil.FILE_SEPARATOR + properties.getBucketName();String MyUrl = dir + FileUtil.FILE_SEPARATOR + fileName;try (FileOutputStream fos = new FileOutputStream(MyUrl)) {// 遍歷每個分片路徑for (RecordFileChunk chunkPath : sortchunks) {try (FileInputStream fis = new FileInputStream(chunkPath.getChunkPath())) {// 緩沖區大小byte[] buffer = new byte[1024];int length;while ((length = fis.read(buffer)) > 0) {// 將分片內容寫入目標文件fos.write(buffer, 0, length);}}}System.out.println("文件合并完成!");} catch (IOException e) {e.printStackTrace();throw new RuntimeException("文件合并失敗!", e);}QueryWrapper<RecordFile> q22 = new QueryWrapper<>();q22.eq("md5",md5);RecordFile fileInfo = recordFileService.getOne(q22);if (fileInfo != null) {fileInfo.setUploadStatus("1"); // 更新文件狀態為已完成fileInfo.setPath(filePath);fileInfo.setType("file");fileInfo.setFileType("upload");fileInfo.setParentId(Long.valueOf(parentId));fileInfo.setName(myName);recordFileService.saveOrUpdate(fileInfo);}return R.ok();}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/76613.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/76613.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/76613.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Unity中的靜態合批使用整理

靜態批處理是一種繪制調用批處理方法&#xff0c;它組合不移動的網格以減少繪制調用。它將組合的網格轉換為世界空間&#xff0c;并為它們構建一個共享頂點和索引緩沖區。然后&#xff0c;對于可見網格&#xff0c;Unity 會執行一系列簡單的繪制調用&#xff0c;每個調用之間幾…

【機器學習中的基本術語:特征、樣本、訓練集、測試集、監督/無監督學習】

機器學習基本術語詳解 1. 特征&#xff08;Feature&#xff09; 定義&#xff1a;數據的屬性或變量&#xff0c;用于描述樣本的某個方面。作用&#xff1a;模型通過學習特征與目標之間的關系進行預測。示例&#xff1a; 預測房價時&#xff0c;特征可以是 面積、地段、房齡。…

C++學習之路:指針基礎

目錄 指針介紹與基本用法雙重指針函數指針空指針與野指針函數參數的指針傳遞最后 指針一般在C/C語言學習的后期接觸&#xff0c;這樣就導致指針給新手一種高深莫測、難以掌握的刻板印象。但實際上指針的使用很簡單&#xff0c;并且還能夠極大的提高程序的靈活性&#xff0c;幫助…

【服務日志鏈路追蹤】

MDCInheritableThreadLocal和spring cloud sleuth 在微服務架構中&#xff0c;日志鏈路追蹤&#xff08;Logback Distributed Tracing&#xff09; 是一個關鍵需求&#xff0c;主要用于跟蹤請求在不同服務間的調用鏈路&#xff0c;便于排查問題。常見的實現方案有兩種&#x…

Kafka+Zookeeper從docker部署到spring boot使用完整教程

文章目錄 一、Kafka1.Kafka核心介紹&#xff1a;?核心架構?核心特性?典型應用 2.Kafka對 ZooKeeper 的依賴&#xff1a;3.去 ZooKeeper 的演進之路&#xff1a;注&#xff1a;&#xff08;本文采用ZooKeeper3.8 Kafka2.8.1&#xff09; 二、Zookeeper1.核心架構與特性2.典型…

JUC系列JMM學習之隨筆

JUC: JUC 是 Java 并發編程的核心工具包,全稱為 Java Util Concurrent,是 java.util.concurrent 包及其子包的簡稱。它提供了一套強大且高效的并發編程工具,用于簡化多線程開發并提高性能。 CPU核心數和線程數的關系:1核處理1線程(同一時間單次) CPU內核結構: 工作內…

The Rust Programming Language 學習 (九)

泛型 每一個編程語言都有高效處理重復概念的工具。在 Rust 中其工具之一就是 泛型&#xff08;generics&#xff09;。泛型是具體類型或其他屬性的抽象替代。我們可以表達泛型的屬性&#xff0c;比如他們的行為或如何與其他泛型相關聯&#xff0c;而不需要在編寫和編譯代碼時知…

藍橋杯 混乘數字

問題描述 混乘數字的定義如下&#xff1a; 對于一個正整數 n&#xff0c;如果存在正整數 a 和 b&#xff0c;使得&#xff1a; n a b且 a 與 b 的十進制數位中每個數字出現的次數之和&#xff0c;與 n 中對應數字出現的次數相同&#xff0c;則稱 n 為混乘數字。 示例 對于…

CExercise04_1位運算符_2 定義一個函數判斷給定的正整數是否為2的冪

題目&#xff1a; 給定一個正整數&#xff0c;請定義一個函數判斷它是否為2的冪(1, 2, 4, 8, 16, …) 分析&#xff1a; &#xff1a; 代碼 #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdbool.h>/* 給定一個正整數&#xff0c;請定義一個函數…

SSL證書不可信的原因有哪些?(國科云)

SSL證書用于在客戶端和服務器端之間建立一條加密通道&#xff0c;確保數據在傳輸過程中的安全性和完整性。然而&#xff0c;在實際應用中&#xff0c;我們有時會遇到SSL證書不可信的情況&#xff0c;嚴重影響了用戶對網站的信任度。那么&#xff0c;SSL證書不可信的原因究竟有哪…

[王陽明代數講義]琴語言類型系統工程特性

琴語言類型系統工程特性 層展物理學組織實務與藝術與琴生生.物機.械科.技工.業研究.所軟凝聚態物理開發工具包社會科學氣質砥礪學人生意氣場社群成員魅力場與心氣微積分社會關系力學 意氣實體過程圖論信息編碼&#xff0c;如來碼導引 注意力機制道裝Transformer架構的發展標度律…

自抗擾ADRC之二階線性擴展狀態觀測器(LESO)推導

1.龍伯格觀測器 實際工程應用中&#xff0c;狀態變量有時難以使用傳感器直接測量&#xff0c;在這種情況下&#xff0c;使用狀態觀測器估計系統實際狀態是非常常見的做法。最出名的狀態觀測器當屬龍伯格博士在1971年發表于TAC的An Introduction to Observer[1]一文中提出的基于…

從頭開發一個Flutter插件(二)高德地圖定位插件

開發基于高德定位SDK的Flutter插件 在上一篇文章里具體介紹了Flutter插件的具體開發流程&#xff0c;從創建項目到發布。接下來將為Flutter天氣項目開發一個基于高德定位SDK的Flutter定位插件。 申請key 首先進入高德地圖定位SDK文檔內下載定位SDK&#xff0c;并按要求申請A…

分布式鎖之redis6

一、分布式鎖介紹 之前我們都是使用本地鎖&#xff08;synchronize、lock等&#xff09;來避免共享資源并發操作導致數據問題&#xff0c;這種是鎖在當前進程內。 那么在集群部署下&#xff0c;對于多個節點&#xff0c;我們要使用分布式鎖來避免共享資源并發操作導致數據問題…

ubuntu中使用安卓模擬器

本文這里介紹 使用 android studio Emulator &#xff0c; 當然也有 Anbox (Lightweight)&#xff0c; Waydroid (Best for Full Android Experience), 首先確保自己安裝了 android studio &#xff1b; sudo apt update sudo apt install openjdk-11-jdk sudo snap install…

二語習得理論(Second Language Acquisition, SLA)如何學習英語

二語習得理論&#xff08;Second Language Acquisition, SLA&#xff09;是研究學習者如何在成人或青少年階段學習第二語言&#xff08;L2&#xff09;的理論框架。該理論主要關注語言習得過程中的認知、社會和文化因素&#xff0c;解釋了學習者如何從初學者逐漸變得流利并能夠…

WinDbg. From A to Z! 筆記(下)

原文鏈接: WinDbg. From A to Z! 文章目錄 使用WinDbg臨界區相關命令示例 -- 查看臨界區其他有用的命令 WinDbg中的偽寄存器自動偽寄存器 WinDbg中的表達式其他操作默認的表達式計算方式 WinDbg中的重命名調試器命令語言編程控制流命令程序執行 WinDbg 遠程調試事件監控WinDbg …

RainbowDash 的旅行

D RainbowDash 的旅行 - 第七屆校賽正式賽 —— 補題 題目大意&#xff1a; 湖中心有一座島&#xff0c;湖的外圍有 m m m 間木屋&#xff08;圍繞小島&#xff09; &#xff0c;第 i i i 間木屋和小島之間有 a i a_i ai? 座 A A A 類橋&#xff0c; b i b_i bi? 座 B …

MySQL-SQL-DDL語句、表結構創建語句

一.SQL SQL&#xff1a;一門操作關系型數據庫的編程語言&#xff0c;定義操作所有關系型數據庫的統一標準 二. DDL-數據庫 1. 查詢所有數據庫 命令&#xff1a;show databases; 2. 查詢當前數據庫 命令&#xff1a;select database(); 3. 創建數據庫 命令&#xff1a;create da…

Sora結構猜測

方案&#xff1a;VAE Encoder&#xff08;視頻壓縮&#xff09; -> Transform Diffusion &#xff08;從視頻數據中學習分布&#xff0c;并根據條件生成新視頻&#xff09; -> VAE Decoder &#xff08;視頻解壓縮&#xff09; 從博客出發&#xff0c;經過學術Survey&am…