news 2026/1/29 15:01:55

HTML5利用Vue2实现大文件分片上传的进度监控界面?

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
HTML5利用Vue2实现大文件分片上传的进度监控界面?

大文件上传方案探索:从WebUploader到自定义分片上传的实践

作为一名前端开发工程师,最近遇到了一个颇具挑战性的需求:需要在Vue项目中实现4GB左右大文件的稳定上传,且要兼容Chrome、Firefox、Edge等主流浏览器,后端使用PHP接收。此前我们采用了百度开源的WebUploader组件,但在实际使用中遇到了几个难以解决的问题:

  1. 分片上传过程中偶尔会出现断点续传失效的情况
  2. 对新版浏览器的兼容性不够理想
  3. 缺乏官方技术支持,社区活跃度下降
  4. 自定义UI的灵活性不足

方案选型思考

经过技术调研,我评估了以下几个主流方案:

  1. Plupload:功能全面但文档不够友好,对Vue集成支持一般
  2. Uppy:现代感强但体积较大,学习曲线较陡
  3. Resumable.js:专注分片上传但UI较为基础
  4. 自定义实现:基于XMLHttpRequest/Fetch API实现核心分片逻辑

最终决定采用自定义分片上传方案,主要基于以下考虑:

  • 完全控制上传流程
  • 可以针对业务需求深度优化
  • 减少第三方依赖,降低维护成本
  • 与Vue生态无缝集成

核心实现思路

1. 前端分片策略

// 文件分片工具函数constchunkFile=(file,chunkSize=5*1024*1024)=>{constchunks=[]letcurrent=0while(current<file.size){chunks.push({file:file.slice(current,current+chunkSize),chunkIndex:chunks.length,totalChunks:Math.ceil(file.size/chunkSize),fileName:file.name,fileSize:file.size,fileType:file.type,fileLastModified:file.lastModified,identifier:generateFileIdentifier(file)// 生成唯一标识用于断点续传})current+=chunkSize}returnchunks}// 生成文件唯一标识(基于文件内容)constgenerateFileIdentifier=(file)=>{returnnewPromise((resolve)=>{constreader=newFileReader()reader.onload=(e)=>{constarr=newUint8Array(e.target.result)consthashArray=Array.from(arr).map(b=>b.toString(16).padStart(2,'0'))resolve(hashArray.join('').substring(0,16))}reader.readAsArrayBuffer(file.slice(0,1024*1024))// 取前1MB计算哈希})}

2. Vue组件实现

export default { data() { return { file: null, chunks: [], uploadStatus: 'idle', // idle, uploading, paused, completed, error progress: 0, error: null, currentChunk: 0, abortController: null } }, methods: { async handleFileChange(e) { this.file = e.target.files[0] if (!this.file) return // 生成文件标识(简化版,实际项目应使用更可靠的算法) const identifier = await this.generateSimpleIdentifier(this.file) // 检查服务器是否有未完成的上传记录 const res = await this.checkUploadStatus(identifier) if (res.exists) { if (confirm('检测到未完成的上传,是否继续?')) { this.currentChunk = res.uploadedChunks } else { // 清除服务器记录(实际项目应实现) } } this.chunks = this.chunkFile(this.file) this.progress = Math.round((this.currentChunk / this.chunks.length) * 100) }, async startUpload() { if (!this.file) return this.uploadStatus = 'uploading' this.error = null this.abortController = new AbortController() try { for (let i = this.currentChunk; i < this.chunks.length; i++) { if (this.uploadStatus !== 'uploading') break // 处理暂停情况 const chunk = this.chunks[i] const formData = new FormData() formData.append('file', chunk.file) formData.append('chunkIndex', chunk.chunkIndex) formData.append('totalChunks', chunk.totalChunks) formData.append('fileName', chunk.fileName) formData.append('fileSize', chunk.fileSize) formData.append('fileType', chunk.fileType) formData.append('identifier', chunk.identifier) await this.uploadChunk(formData) this.currentChunk = i + 1 this.progress = Math.round(((i + 1) / this.chunks.length) * 100) } if (this.uploadStatus === 'uploading') { await this.mergeChunks(this.chunks[0].identifier, this.chunks[0].fileName) this.uploadStatus = 'completed' this.$emit('upload-complete') } } catch (err) { console.error('上传失败:', err) this.error = err.message || '上传过程中出现错误' this.uploadStatus = 'error' } }, async uploadChunk(formData) { return fetch('/api/upload-chunk.php', { method: 'POST', body: formData, signal: this.abortController.signal }) }, async mergeChunks(identifier, fileName) { return fetch('/api/merge-chunks.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ identifier, fileName }) }) }, // 简化版标识生成(实际项目应使用更可靠的算法) generateSimpleIdentifier(file) { return `${file.name}-${file.size}-${file.lastModified}` }, async checkUploadStatus(identifier) { // 实际项目应实现与后端的交互 return { exists: false, uploadedChunks: 0 } }, pauseUpload() { if (this.uploadStatus === 'uploading') { this.abortController.abort() this.uploadStatus = 'paused' } }, resumeUpload() { if (this.uploadStatus === 'paused') { this.startUpload() } } } }

3. PHP后端实现要点

$chunkIndex,'totalChunks'=>$totalChunks,'fileName'=>$fileName,'uploaded'=>time()]));echojson_encode(['status'=>'success']);}else{http_response_code(500);echojson_encode(['status'=>'error','message'=>'Failed to save chunk']);}// merge-chunks.php - 合并分片header('Content-Type: application/json');$uploadDir='/path/to/upload/dir/';$tempDir=$uploadDir.'temp/';$data=json_decode(file_get_contents('php://input'),true);$identifier=$data['identifier']??'';$fileName=$data['fileName']??'';// 检查标识符和文件名if(empty($identifier)||empty($fileName)){http_response_code(400);echojson_encode(['status'=>'error','message'=>'Invalid parameters']);exit;}// 检查上传状态文件$statusFile=$tempDir.$identifier.'.upload';if(!file_exists($statusFile)){http_response_code(404);echojson_encode(['status'=>'error','message'=>'Upload not found']);exit;}$status=json_decode(file_get_contents($statusFile),true);$totalChunks=$status['totalChunks']??0;// 合并文件$finalPath=$uploadDir.$fileName;if($fp=fopen($finalPath,'wb')){for($i=0;$i<$totalChunks;$i++){$chunkPath=$tempDir.$identifier.'_'.$i;if(!file_exists($chunkPath)){fclose($fp);unlink($finalPath);// 删除已创建的部分文件http_response_code(400);echojson_encode(['status'=>'error','message'=>'Missing chunk '.$i]);exit;}$content=file_get_contents($chunkPath);fwrite($fp,$content);unlink($chunkPath);// 删除已合并的分片}fclose($fp);// 删除状态文件unlink($statusFile);echojson_encode(['status'=>'success','path'=>$finalPath]);}else{http_response_code(500);echojson_encode(['status'=>'error','message'=>'Failed to create final file']);}?>

方案优势与改进点

优势

  1. 完全可控:从分片策略到上传逻辑完全自主实现
  2. 深度优化:可以根据网络状况动态调整分片大小
  3. 良好兼容:基于标准Web API实现,兼容所有现代浏览器
  4. 断点续传:通过文件标识实现可靠的断点续传
  5. 进度可视化:精确计算上传进度

可改进方向

  1. 并发上传:当前实现是顺序上传,可优化为并发上传提高速度
  2. 文件校验:增加MD5/SHA校验确保文件完整性
  3. 更可靠的标识生成:当前简化版标识可能存在冲突风险
  4. 服务端清理:实现自动清理未完成上传的临时文件
  5. 拖拽上传:增强用户体验,支持拖放文件上传

实施建议

  1. 渐进式实现:先实现基本分片上传,再逐步添加断点续传、并发上传等功能
  2. 充分测试:在不同网络环境和浏览器下进行全面测试
  3. 监控上报:添加上传失败监控和错误上报机制
  4. 性能优化:根据实际测试结果调整分片大小和并发数

通过这种自定义实现方式,我们成功解决了WebUploader带来的各种问题,同时获得了更好的性能和更灵活的控制能力。目前该方案已在我们项目中稳定运行数月,处理了数百个4GB+文件的上传,未出现重大故障。

将组件复制到项目中

示例中已经包含此目录

引入组件

配置接口地址

接口地址分别对应:文件初始化,文件数据上传,文件进度,文件上传完毕,文件删除,文件夹初始化,文件夹删除,文件列表
参考:http://www.ncmem.com/doc/view.aspx?id=e1f49f3e1d4742e19135e00bd41fa3de

处理事件

启动测试

启动成功

效果

数据库

效果预览

文件上传

文件刷新续传

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

文件夹上传

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

批量下载

支持文件批量下载

下载续传

文件下载支持离线保存进度信息,刷新页面,关闭页面,重启系统均不会丢失进度信息。

文件夹下载

支持下载文件夹,并保留层级结构,不打包,不占用服务器资源。

下载示例

点击下载完整示例

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/1/27 6:43:13

多策略混合改进麻雀搜索算法:探索优化新境界

多策略混合改进改进麻雀搜索算法 下面性能图展现了23种的其中8个测试函数&#xff0c;23个标准测试函数全部都有 改进点&#xff1a;附带参考文献来源 1、sin混沌映射进行种群初始化 2、采用动态自适应权重优化发现者位置。 3、柯西变异 发挥柯西算子的扰动能力&#xff0c;提高…

作者头像 李华
网站建设 2026/1/27 10:47:41

智慧农业智能水肥灌溉控制系统——精准种植与绿色生产解决方案

一、方案背景在农业现代化转型进程中&#xff0c;传统灌溉施肥模式面临水资源浪费(利用率不足 50%)、化肥过量施用(利用率仅 30%-40%)、人工成本高企、作物品质不均等突出问题&#xff0c;同时引发土壤板结、水体污染等生态隐患。本智能水肥灌溉控制系统融合物联网、大数据、精…

作者头像 李华
网站建设 2026/1/28 6:15:51

【攻防世界】reverse | hackme 详细题解 WP

【攻防世界】reverse | hackme 详细题解 WP 下载附件sub_400F8E函数伪代码&#xff1a; __int64 __fastcall sub_400F8E(__int64 a1, int a2, int a3, int a4, int a5, int a6) {int v6; // edxint v7; // ecxint v8; // r8dint v9; // r9dint v10; // ecxint v11; // r8dint v…

作者头像 李华
网站建设 2026/1/28 1:53:12

仅限前沿研究者:量子 Agent 最新7种优化策略首次公开

第一章&#xff1a;量子 Agent 算法优化的前沿概览随着量子计算与人工智能的深度融合&#xff0c;量子 Agent 在复杂环境中的决策优化展现出前所未有的潜力。这类智能体利用量子态叠加、纠缠和干涉等特性&#xff0c;在搜索空间巨大的任务中实现指数级加速&#xff0c;尤其在动…

作者头像 李华
网站建设 2026/1/28 1:42:19

【计算机毕业设计案例】基于springboot+微信小程序的的提交交通违法有奖曝光平台(程序+文档+讲解+定制)

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

作者头像 李华
网站建设 2026/1/29 4:32:39

vue和springboot框架开发的小程序 宠物领养系统_c27l9jc8

文章目录具体实现截图主要技术与实现手段关于我本系统开发思路java类核心代码部分展示结论源码lw获取/同行可拿货,招校园代理 &#xff1a;文章底部获取博主联系方式&#xff01;具体实现截图 同行可拿货,招校园代理 vueSpringboot宠物领养系统_车7 框架开发的小程序和 …

作者头像 李华