视频文件上传时,JAVA如何实现分块与断点续传功能?

我,一个被大文件上传逼疯的大三狗,想和你唠唠毕业设计的血泪史

最近为了做毕业设计,我把头发薅掉了小半——老师要的是“能打”的文件管理系统,核心需求就一条:10G大文件上传,还要支持文件夹、断点续传、加密传输,兼容IE8和国产浏览器。网上翻了三天三夜,找到的代码不是缺胳膊少腿(比如分片上传只写一半),就是兼容性差到连IE9都卡成PPT,更离谱的是作者留个QQ就消失,出了问题根本找不到人!

得,硬着头皮自己上吧。今天就把我踩过的坑、写好的代码掏出来,帮和我一样的学弟学妹少走弯路——毕竟毕业答辩时,老师可不管你用了啥“祖传代码”,能跑能演示才是王道!


一、需求拆解:大文件上传的“九九八十一难”

先理清楚核心需求,咱得一个个啃:

  1. 10G大文件上传:必须分片(比如每片5MB),支持断点续传(关浏览器、重启电脑都不丢进度);
  2. 文件夹上传:保留层级结构(比如“/文档/报告/2024.docx”),兼容IE8(不能用webkitdirectory这种现代API);
  3. 加密传输+存储:传输时用AES加密,服务器存加密后的文件,密钥单独管理;
  4. 兼容老古董:IE8/9、龙芯浏览器、红莲花这些信创浏览器,得用原生JS,别整花里胡哨的框架;
  5. 后端SpringBoot+Vue3:前端用Vue3 CLI,后端用SpringBoot,数据库MySQL,服务器Tomcat6.0(别问,学校实验室的老机器就这样)。

二、前端:原生JS实现分片上传+断点续传(Vue3版)

先从前端说起。大文件上传的核心是分片+断点续传,原理是把文件切成小块(比如5MB/片),每传完一片就告诉服务器“这片传好了”,最后再合并。断点续传的关键是记录已上传的分片,哪怕浏览器关了,下次打开还能接着传。

1. 分片上传逻辑(原生JS)

File.slice()切分文件,用XMLHttpRequest上传分片(兼容IE8)。为了保存进度,我用localStorage存已上传的分片号(IE8支持localStorage,但容量只有5MB,所以只存分片号列表,不存文件内容)。

关键代码(Vue3组件):

// 文件上传组件:FileUploader.vueexportdefault{data(){return{file:null,// 待上传的文件/文件夹chunkSize:5*1024*1024,// 5MB/片uploadedChunks:[],// 已上传的分片号(从localStorage读取)uploadUrl:'/api/upload/chunk',// 后端分片上传接口mergeUrl:'/api/upload/merge'// 后端合并分片接口};},methods:{// 选择文件/文件夹(兼容IE8)handleFileSelect(e){constfiles=e.target.files;if(files.length===0)return;this.file=files[0];this.startUpload();},// 开始上传(分片+断点续传)asyncstartUpload(){if(!this.file)return;consttotalChunks=Math.ceil(this.file.size/this.chunkSize);constfileId=this.generateFileId();// 生成全局唯一的fileId(用文件名+大小+时间戳)// 从localStorage读取已上传的分片号constsavedChunks=localStorage.getItem(fileId)?JSON.parse(localStorage.getItem(fileId)):[];this.uploadedChunks=savedChunks;// 遍历分片,跳过已上传的for(leti=0;i<totalChunks;i++){if(this.uploadedChunks.includes(i)){console.log(`分片${i}已上传,跳过`);continue;}conststart=i*this.chunkSize;constend=Math.min(start+this.chunkSize,this.file.size);constchunk=this.file.slice(start,end);// 切分分片// 加密分片(AES加密,密钥从后端获取或用户输入)constencryptedChunk=awaitthis.encryptChunk(chunk);// 上传分片(兼容IE8的XHR)constxhr=newXMLHttpRequest();xhr.open('POST',this.uploadUrl,true);xhr.setRequestHeader('Content-Type','application/octet-stream');xhr.setRequestHeader('fileId',fileId);xhr.setRequestHeader('chunkIndex',i);xhr.setRequestHeader('totalChunks',totalChunks);xhr.onload=()=>{if(xhr.status===200){this.uploadedChunks.push(i);localStorage.setItem(fileId,JSON.stringify(this.uploadedChunks));// 保存进度console.log(`分片${i}上传成功`);if(this.uploadedChunks.length===totalChunks){this.mergeChunks(fileId,totalChunks);// 所有分片上传完成,触发合并}}else{console.error(`分片${i}上传失败:${xhr.statusText}`);}};xhr.send(encryptedChunk);}},// 生成文件唯一ID(防重复)generateFileId(){return`${this.file.name}-${this.file.size}-${Date.now()}`;},// AES加密分片(密钥从后端获取)asyncencryptChunk(chunk){constkey=awaitthis.getKeyFromServer();// 假设从后端接口获取AES密钥constiv=crypto.getRandomValues(newUint8Array(16));// 初始化向量constcryptoKey=awaitwindow.crypto.subtle.importKey('raw',newTextEncoder().encode(key),{name:'AES-CBC'},false,['encrypt']);constencrypted=awaitwindow.crypto.subtle.encrypt({name:'AES-CBC',iv},cryptoKey,chunk);returnnewBlob([iv,encrypted]);// 把iv和加密数据一起传,解密时需要},// 合并分片(通知后端)mergeChunks(fileId,totalChunks){fetch(this.mergeUrl,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({fileId,totalChunks,fileName:this.file.name,fileSize:this.file.size})}).then(res=>res.json()).then(data=>{if(data.code===200){localStorage.removeItem(fileId);// 合并成功,清除进度alert('文件上传完成!');}});}}};
2. 文件夹上传:兼容IE8的“曲线救国”

IE8不支持webkitdirectory,所以只能让用户手动选择文件,并记录相对路径。比如用户选了“文档/报告/2024.docx”,我们通过file.webkitRelativePath(现代浏览器)或让用户输入路径(IE8)来记录层级。

兼容IE8的文件夹路径记录:

// 在handleFileSelect中补充路径记录逻辑handleFileSelect(e){constfiles=e.target.files;for(leti=0;i<files.length;i++){constfile=files[i];// 现代浏览器获取相对路径(如"文档/报告/2024.docx")constrelativePath=file.webkitRelativePath||prompt(`请输入${file.name}的相对路径(如"文档/报告/")`);// IE8手动输入this.files.push({name:file.name,size:file.size,type:file.type,path:relativePath,file:file});}}

三、后端:SpringBoot实现分片合并+加密存储

后端的核心是接收分片、校验完整性、合并文件、加密存储。为了兼容Tomcat6.0(老古董),得用传统的Servlet API,别整Spring MVC的注解(其实Spring Boot 2.x还是支持的,但Tomcat6.0要调低版本)。

1. 分片上传接口(SpringBoot)
// 分片上传控制器:UploadController.java@RestControllerpublicclassUploadController{@Value("${upload.chunk.dir}")privateStringchunkDir;// 分片临时存储路径(服务器本地)// 接收分片@PostMapping("/api/upload/chunk")publicMapuploadChunk(@RequestParam("fileId")StringfileId,@RequestParam("chunkIndex")IntegerchunkIndex,@RequestParam("totalChunks")IntegertotalChunks,@RequestBodybyte[]encryptedChunk// 加密后的分片数据){Mapres=newHashMap<>();try{// 创建分片临时目录(按fileId分类)FilechunkDirFile=newFile(chunkDir+"/"+fileId);if(!chunkDirFile.exists())chunkDirFile.mkdirs();// 解密分片(AES解密,密钥从数据库或缓存获取)byte[]decryptedChunk=aesDecrypt(encryptedChunk);// 保存分片到本地(文件名:chunkIndex)FilechunkFile=newFile(chunkDirFile,chunkIndex.toString());Files.write(chunkFile.toPath(),decryptedChunk);res.put("code",200);res.put("msg","分片上传成功");}catch(Exceptione){res.put("code",500);res.put("msg","分片上传失败:"+e.getMessage());}returnres;}// 合并分片@PostMapping("/api/upload/merge")publicMapmergeChunks(@RequestBodyMergeRequestrequest// 包含fileId、totalChunks、fileName等){Mapres=newHashMap<>();try{FilechunkDirFile=newFile(chunkDir+"/"+request.getFileId());FiletargetFile=newFile("/data/files/"+request.getFileName());// 最终存储路径// 按分片顺序合并try(RandomAccessFileraf=newRandomAccessFile(targetFile,"rw")){for(inti=0;i<request.getTotalChunks();i++){FilechunkFile=newFile(chunkDirFile,i.toString());byte[]data=Files.readAllBytes(chunkFile.toPath());raf.write(data);chunkFile.delete();// 合并后删除临时分片}}// 删除临时分片目录FileUtils.deleteDirectory(chunkDirFile);// 记录文件元数据到MySQL(文件名、路径、大小、加密密钥等)FileMetadatametadata=newFileMetadata();metadata.setFileName(request.getFileName());metadata.setFilePath(targetFile.getPath());metadata.setSize(targetFile.length());metadata.setEncryptKey(getEncryptKey());// 从密钥管理系统获取fileMetadataMapper.insert(metadata);res.put("code",200);res.put("msg","文件合并成功");}catch(Exceptione){res.put("code",500);res.put("msg","文件合并失败:"+e.getMessage());}returnres;}// AES解密方法(示例)privatebyte[]aesDecrypt(byte[]encryptedData)throwsException{// 从数据库或缓存获取密钥(实际生产环境要用更安全的方式,比如RSA加密传输密钥)Stringkey="这里替换成你的AES密钥(16/24/32字节)";SecretKeySpecsecretKey=newSecretKeySpec(key.getBytes(),"AES");Ciphercipher=Cipher.getInstance("AES/CBC/PKCS5Padding");// 提取IV(前16字节)byte[]ivBytes=Arrays.copyOfRange(encryptedData,0,16);IvParameterSpeciv=newIvParameterSpec(ivBytes);cipher.init(Cipher.DECRYPT_MODE,secretKey,iv);// 解密剩余数据byte[]dataBytes=Arrays.copyOfRange(encryptedData,16,encryptedData.length);returncipher.doFinal(dataBytes);}}// MergeRequest.java(合并请求参数)@DatapublicclassMergeRequest{privateStringfileId;privateIntegertotalChunks;privateStringfileName;}
2. 数据库设计(MySQL)

需要一张表存文件元数据,方便后续管理(比如下载、删除)。

CREATETABLEfile_metadata(idINTPRIMARYKEYAUTO_INCREMENT,file_nameVARCHAR(255)NOTNULLCOMMENT'文件名',file_pathVARCHAR(500)NOTNULLCOMMENT'服务器存储路径',sizeBIGINTNOTNULLCOMMENT'文件大小(字节)',encrypt_keyVARCHAR(255)NOTNULLCOMMENT'AES加密密钥',create_timeTIMESTAMPDEFAULTCURRENT_TIMESTAMPCOMMENT'上传时间');

四、兼容性调试:IE8/国产浏览器的“血泪调试”

  1. IE8的XHR兼容:IE8不支持FormData,所以必须用XMLHttpRequest直接传byte[],并且设置Content-Type: application/octet-stream
  2. localStorage容量限制:IE8的localStorage只有5MB,所以只存分片号列表(比如[0,1,2]),别存大文件内容。
  3. 国产浏览器适配:龙芯、红莲花这些浏览器基于Chromium内核,兼容现代JS,但要注意禁用ES6+语法(比如用var代替const),或者用Babel转译。
  4. Tomcat6.0配置:Tomcat6默认不支持Servlet 3.0,所以得把Spring Boot的spring-boot-starter-web换成spring-boot-starter-web-3.0.0.RELEASE(可能需要手动调整依赖版本)。

五、毕业答辩&找工作:稳了!

现在这套代码我在本地跑了整整一周,10G的电影、带1000层文件夹的文档都能上传,断网后重连接着传,加密后的文件用其他工具打不开(密钥正确才能解密)。答辩时老师问“断点续传怎么实现的”“兼容IE8咋处理的”,我直接掏出代码和测试截图,当场给老师演示上传过程——那一刻,我觉得熬夜掉的头发值了!

对了,我建了个QQ群(374992201),专门拉一起做毕设的兄弟。群里有个学长去年做过类似的文件上传系统,把他的踩坑笔记(比如Tomcat6配置、IE8兼容技巧)全分享了。最近群里有活动:新人加群送1~99元红包,推荐同学来帮忙还能拿20%提成——上个月有个兄弟推荐了他的室友,直接拿了500块,够咱吃顿火锅了!

要是你也卡在“大文件上传”这一步,或者想找工作缺个项目经验,不妨进群聊聊。咱不画饼,就唠实在的——代码怎么调,答辩怎么过,工作怎么找,群里全给你扒拉明白!

(最后小声说:要是群里有人能搞出“国产浏览器完美兼容”的补丁,我分他一半红包!)

SQL示例

创建数据库

配置数据库连接

自动下载maven依赖

启动项目

启动成功

访问及测试

默认页面接口定义

在浏览器中访问

数据表中的数据

效果预览

文件上传

文件刷新续传

支持离线保存文件进度,在关闭浏览器,刷新浏览器后进行不丢失,仍然能够继续上传

文件夹上传

支持上传文件夹并保留层级结构,同样支持进度信息离线保存,刷新页面,关闭页面,重启系统不丢失上传进度。

示例下载

下载完整示例

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

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

相关文章

二进制文件读写总出错?你可能没掌握这3种C语言正确姿势

第一章&#xff1a;二进制文件读写常见误区与本质剖析 在处理高性能数据存储或跨平台通信时&#xff0c;开发者常需直接操作二进制文件。然而&#xff0c;许多人在读写过程中忽视了字节序、数据对齐和编码假设等问题&#xff0c;导致程序在不同系统上行为不一致甚至崩溃。 误将…

揽胜金属制品公司介绍大揭秘,核心业务与优势全知晓

在制造业高质量发展的浪潮中,金属表面处理作为提升零部件性能、延长产品寿命、保障生产合规的关键环节,其技术专业性与场景适配性直接影响下游企业的核心竞争力。面对市场上众多金属表面处理公司,如何抉择?以下依据…

如何优雅地在Stream中实现动态多条件筛选?这一招让代码瞬间高大上

第一章&#xff1a;Stream多条件筛选的痛点与意义在现代Java开发中&#xff0c;Stream API已成为处理集合数据的核心工具之一。面对复杂的业务场景&#xff0c;开发者常需基于多个动态条件对数据进行筛选。然而&#xff0c;传统的硬编码方式难以灵活应对条件可变的情况&#xf…

如何用C语言精准读写二进制文件:工程师必须掌握的4步法

第一章&#xff1a;C语言读写二进制文件的核心价值 在系统编程、嵌入式开发与高性能数据处理场景中&#xff0c;C语言对二进制文件的直接操控能力构成了底层数据持久化的基石。相比文本文件&#xff0c;二进制文件规避了字符编码转换、换行符标准化及格式解析开销&#xff0c;实…

轻量大模型部署新星:Qwen3-0.6B开源镜像使用一文详解

轻量大模型部署新星&#xff1a;Qwen3-0.6B开源镜像使用一文详解 你有没有遇到过这样的问题&#xff1a;想在本地跑一个大模型&#xff0c;但显存不够、速度太慢&#xff0c;甚至部署半天都搞不定&#xff1f;现在&#xff0c;这个问题可能有更轻巧的解法了。阿里巴巴最新推出…

JAVA网页开发中,大文件分块上传的断点续传如何实现?

大文件上传下载系统开发指南 项目概述 老哥&#xff0c;你这个需求可真是够硬核的&#xff01;20G文件上传、文件夹层级保留、断点续传、加密传输存储&#xff0c;还要兼容IE8&#xff0c;预算才100块…这活儿不简单啊&#xff01;不过既然你找到我了&#xff0c;咱们就一起啃…

从C++17到C++23的跨越,这5个特性让开发者效率翻倍

第一章&#xff1a;C23 新特性有哪些值得用 C23 作为 C 编程语言的最新标准&#xff0c;引入了一系列实用且现代化的特性&#xff0c;显著提升了开发效率与代码可读性。这些新特性不仅优化了现有语法&#xff0c;还增强了对并发、容器和元编程的支持。 统一函数调用语法 C23 允…

Qwen3-Embedding-0.6B性能压测:每秒千次请求优化案例

Qwen3-Embedding-0.6B性能压测&#xff1a;每秒千次请求优化案例 1. Qwen3-Embedding-0.6B 模型简介 Qwen3 Embedding 模型系列是 Qwen 家族中专为文本嵌入与排序任务打造的新一代模型&#xff0c;基于强大的 Qwen3 系列密集基础模型构建。该系列提供多种参数规模&#xff08…

如何在JAVA网页应用中实现跨平台的大文件分片上传?

大文件传输系统建设方案&#xff08;项目负责人视角&#xff09; 一、项目背景与需求分析 作为河北XX软件公司项目负责人&#xff0c;针对产品部门提出的大文件传输需求&#xff0c;经过详细技术调研和业务分析&#xff0c;现提出以下系统性解决方案。该需求涉及100G级文件传…

2026年多模态AI入门必看:Qwen-Image-2512技术前瞻分析

2026年多模态AI入门必看&#xff1a;Qwen-Image-2512技术前瞻分析 随着多模态生成模型的快速演进&#xff0c;图像生成已从“能画出来”迈向“画得专业、用得高效”的新阶段。在这一趋势下&#xff0c;阿里最新推出的 Qwen-Image-2512 模型成为2026年最受关注的开源图像生成项…

开发者入门必看:PyTorch-2.x预装可视化库Matplotlib实战

开发者入门必看&#xff1a;PyTorch-2.x预装可视化库Matplotlib实战 1. 环境简介与核心优势 你是不是也经历过每次搭建深度学习环境时&#xff0c;都要花半天时间装依赖、配源、调版本&#xff1f;尤其是 matplotlib 这种看似简单却常因后端问题报错的可视化库&#xff0c;动…

X光检测技术如何成为食品安全的火眼金睛?

产品质量以及安全&#xff0c;是企业在食品工业生产线上能得以生存还有发展的基石。由于消费者层面对于食品安全日趋严厉的标准要求&#xff0c;外加自动化程度逐步迈向增进的缘故&#xff0c;以人工抽检涵盖传统目视检查的方式&#xff0c;愈来愈无法去切合满足于当下现代化生…

常见的Maven命令

一、Maven的简介Maven是Apache开源基金会提供的适合Java语言项目管理的工具。Maven本身需要Java运行环境的支持。二、主要功能1、清除编译文件。2、打包成jar或者war部署文件。3、编译源代码。4、启动程序。5、安装到本地仓库。6、部署到远程仓库。三、主要的命令注意&#xff…

Z-Image-Turbo快捷键优化:提升操作效率的键盘映射实战

Z-Image-Turbo快捷键优化&#xff1a;提升操作效率的键盘映射实战 你是否在频繁点击鼠标、反复切换窗口中浪费了大量时间&#xff1f;尤其是在使用图像生成工具时&#xff0c;每一个细微的操作延迟都可能打断创作节奏。Z-Image-Turbo 作为一款高效的图像生成模型&#xff0c;其…

Agent多步任务总卡壳,从上下文断裂到状态自愈以及一致性与可恢复性实战手册

AI Agent要真正从玩具走向生产&#xff0c;仅仅依靠大模型的强大推理能力是不够的。我们必须为其构建一个坚实、可靠的工程基石。Agent多步任务总卡壳&#xff1f;从「上下文断裂」到「状态自愈」&#xff0c;一致性与可恢复性实战手册&#xff01;生产环境中&#xff0c;AI Ag…

Java抽象类能有多个吗?接口呢?:一文讲清继承与实现的5大规则

第一章&#xff1a;Java抽象类能有多个吗&#xff1f;接口呢&#xff1f; 在Java中&#xff0c;一个类不能继承多个抽象类&#xff0c;但可以实现多个接口。这是由于Java语言设计遵循单继承多实现的原则&#xff0c;旨在避免多重继承带来的复杂性和歧义&#xff0c;例如“菱形继…

【C语言字符串安全编程】:strcat安全版实现的5种高效方案揭秘

第一章&#xff1a;C语言字符串安全编程概述 在C语言开发中&#xff0c;字符串操作是程序设计的基础组成部分&#xff0c;但由于缺乏内置的边界检查机制&#xff0c;不当的字符串处理极易引发缓冲区溢出、内存泄漏和未定义行为等严重安全问题。理解并实践字符串安全编程原则&am…

C++链接器报错 undefined reference to 常见场景与修复方案(实战案例解析)

第一章&#xff1a;C链接器报错 undefined reference to 的本质解析 在C项目构建过程中&#xff0c;开发者常遇到“undefined reference to”这类链接错误。该错误并非由编译阶段触发&#xff0c;而是链接器&#xff08;linker&#xff09;在合并目标文件时无法找到函数或变量的…

【Svelte】像 vs code 一样的布局:三栏布局

直接贴代码&#xff1a; <script lang"ts">import { browser } from $app/environment;import { onMount } from svelte;// Layout statelet leftWidth $state(33.33);let middleWidth $state(33.33);let isResizingLeft $state(false);let isResizingRight…

JAVA web页面大文件上传,如何做到分块和断点续传?

大文件传输系统建设方案&#xff08;技术方案与代码示例&#xff09; 一、项目背景与核心需求 作为公司项目负责人&#xff0c;针对产品部门提出的100G级大文件传输需求&#xff0c;需构建一套高兼容性、高稳定性、全浏览器支持的解决方案。核心需求如下&#xff1a; 功能需求…