vue大文件上传的教程:从原理到实战案例分享

一个大三学生的文件管理系统血泪史(前端篇)

各位看官,我是浙江某高校网络工程专业的大三学生,最近在搞一个"史诗级"项目——文件管理系统。为啥说是史诗级?因为光是需求就快把我整秃噜皮了!

项目背景

我们导师说:“小张啊,现在找工作得有作品,你做个文件管理系统吧,要支持10G大文件上传、加密传输、断点续传、文件夹层级保留,还要兼容IE8!”

我:“老师,您这是要让我重写百度网盘吗?”

导师:“不,我要你超越百度网盘!”

我:“…”

技术选型

经过一番挣扎,我选择了:

  • 前端:Vue3 + 原生JS(因为导师说不能用jQuery)
  • 上传组件:WebUploader(兼容性好) + H5(现代浏览器用)
  • 开发工具:VSCode(因为Sublime Text要钱)
  • 数据库:MySQL(因为导师说这个最简单)
  • 服务器:本地Windows 7 + IE9(学校老机器的配置)

前端实现(血泪版)

1. 兼容性处理

首先得解决IE8兼容问题,我写了这么个神器:

// 检测浏览器并给出友好提示functiondetectBrowser(){constuserAgent=navigator.userAgent;if(userAgent.indexOf('MSIE')>-1||userAgent.indexOf('Trident')>-1){constversion=userAgent.match(/(MSIE |rv:)(\d+)/)[2];if(version<9){alert('检测到您使用的是IE'+version+',本系统需要IE9+或其他现代浏览器!');// 偷偷给IE用户显示个可爱图片document.body.innerHTML=`亲爱的IE用户 请使用Chrome/Firefox/Edge/Safari等现代浏览器`;}}}detectBrowser();

2. 文件上传组件封装

classFileUploader{constructor(options){this.chunkSize=options.chunkSize||5*1024*1024;// 默认5MB分片this.fileInput=document.getElementById(options.inputId);this.uploadBtn=document.getElementById(options.uploadBtnId);this.progressBar=document.getElementById(options.progressId);this.fileList=[];this.initEvents();}initEvents(){this.fileInput.addEventListener('change',(e)=>this.handleFileSelect(e));this.uploadBtn.addEventListener('click',()=>this.startUpload());}handleFileSelect(e){constfiles=Array.from(e.target.files);this.fileList=files.map(file=>({file,chunks:[],uploadedChunks:0,totalChunks:Math.ceil(file.size/this.chunkSize),fileId:this.generateFileId()}));// 显示文件列表(简化版)console.log('已选择文件:',this.fileList.map(f=>f.file.name));}generateFileId(){return'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g,function(c){constr=Math.random()*16|0;return(c==='x'?r:(r&0x3|0x8)).toString(16);});}// 加密函数(简化版)encryptData(data){// 实际项目中应该用AES等加密算法// 这里只是演示,返回Base64编码returnbtoa(unescape(encodeURIComponent(data)));}// 分片上传asyncuploadChunk(fileObj,chunkIndex){constfile=fileObj.file;conststart=chunkIndex*this.chunkSize;constend=Math.min(file.size,start+this.chunkSize);constchunk=file.slice(start,end);// 加密分片数据constencryptedChunk=this.encryptData(chunk);// 这里应该用XMLHttpRequest或Fetch API发送到后端// 但导师说后端要我自己写,所以我只能...console.log(`模拟上传分片${chunkIndex+1}/${fileObj.totalChunks}:${file.name}`);// 模拟网络延迟awaitnewPromise(resolve=>setTimeout(resolve,500));return{success:true,chunkIndex};}asyncstartUpload(){if(this.fileList.length===0){alert('请先选择文件!');return;}for(constfileObjofthis.fileList){try{for(leti=0;i<fileObj.totalChunks;i++){constresult=awaitthis.uploadChunk(fileObj,i);if(result.success){fileObj.uploadedChunks++;this.updateProgress(fileObj);// 保存上传进度到localStorage(断点续传)this.saveProgressToStorage(fileObj);}}console.log(`文件${fileObj.file.name}上传完成!`);}catch(error){console.error(`上传文件${fileObj.file.name}时出错:`,error);}}}updateProgress(fileObj){constpercent=(fileObj.uploadedChunks/fileObj.totalChunks*100).toFixed(2);this.progressBar.style.width=percent+'%';this.progressBar.textContent=percent+'%';}saveProgressToStorage(fileObj){constprogressData={fileId:fileObj.fileId,fileName:fileObj.file.name,uploadedChunks:fileObj.uploadedChunks,totalChunks:fileObj.totalChunks,fileSize:fileObj.file.size,lastModified:fileObj.file.lastModified};localStorage.setItem(`uploadProgress_${fileObj.fileId}`,JSON.stringify(progressData));}// 从localStorage恢复上传进度staticrestoreProgress(){constprogressItems={};for(leti=0;i<localStorage.length;i++){constkey=localStorage.key(i);if(key.startsWith('uploadProgress_')){constdata=JSON.parse(localStorage.getItem(key));progressItems[key]=data;}}returnprogressItems;}}// 使用示例document.addEventListener('DOMContentLoaded',()=>{constuploader=newFileUploader({inputId:'fileInput',uploadBtnId:'uploadBtn',progressId:'progressBar'});// 恢复上传进度constprogress=FileUploader.restoreProgress();console.log('恢复的上传进度:',progress);});

3. Vue3组件封装

export default { name: 'FileUploader', data() { return { files: [], chunkSize: 5 * 1024 * 1024, // 5MB uploadProgress: {} }; }, methods: { handleFileChange(e) { const files = Array.from(e.target.files); // 处理文件夹上传(WebKit browsers) if (files.length === 0 && e.target.webkitEntries) { const entries = Array.from(e.target.webkitEntries); this.processDirectoryEntries(entries); return; } this.files = files.map(file => ({ file, name: file.name, size: file.size, uploaded: false, uploadedSize: 0 })); }, processDirectoryEntries(entries) { // 实际项目中需要递归处理文件夹结构 // 这里简化处理,只取文件 const files = []; const processEntry = (entry) => { if (entry.isFile) { entry.file(file => { files.push(file); if (files.length === entries.length) { this.files = files.map(file => ({ file, name: file.name, size: file.size, uploaded: false, uploadedSize: 0 })); } }); } }; entries.forEach(processEntry); }, async startUpload() { if (this.files.length === 0) { alert('请先选择文件或文件夹!'); return; } for (const fileObj of this.files) { try { await this.uploadFile(fileObj); fileObj.uploaded = true; } catch (error) { console.error(`上传文件 ${fileObj.name} 时出错:`, error); } } }, async uploadFile(fileObj) { const file = fileObj.file; const totalChunks = Math.ceil(file.size / this.chunkSize); for (let i = 0; i < totalChunks; i++) { const start = i * this.chunkSize; const end = Math.min(file.size, start + this.chunkSize); const chunk = file.slice(start, end); // 加密分片(实际项目中应该用更安全的加密方式) const encryptedChunk = this.simpleEncrypt(chunk); // 这里应该调用后端API上传分片 // 但导师说后端要我自己写,所以我只能... console.log(`上传分片 ${i + 1}/${totalChunks}: ${file.name}`); // 模拟网络延迟 await new Promise(resolve => setTimeout(resolve, 300)); fileObj.uploadedSize = end; this.updateProgress(); } }, simpleEncrypt(data) { // 超级简单的"加密"(实际项目不能用!) return data; // 这里应该返回加密后的数据 }, updateProgress() { const totalUploaded = this.files.reduce((sum, file) => sum + file.uploadedSize, 0); const totalSize = this.files.reduce((sum, file) => sum + file.size, 0); const percent = Math.round((totalUploaded / totalSize) * 100); this.$refs.progressBar.style.width = percent + '%'; this.$refs.progressBar.nextElementSibling.textContent = percent + '%'; }, restoreUpload() { // 从localStorage恢复上传进度 const progressItems = {}; for (let i = 0; i < localStorage.length; i++) { const key = localStorage.key(i); if (key.startsWith('uploadProgress_')) { const data = JSON.parse(localStorage.getItem(key)); progressItems[key] = data; } } console.log('恢复的上传进度:', progressItems); alert('上传进度已恢复(控制台查看详情)'); }, formatFileSize(bytes) { if (bytes === 0) return '0 Bytes'; const k = 1024; const sizes = ['Bytes', 'KB', 'MB', 'GB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; } }, mounted() { // 检测浏览器兼容性 this.detectBrowser(); }, methods: { detectBrowser() { const userAgent = navigator.userAgent; if (userAgent.indexOf('MSIE') > -1 || userAgent.indexOf('Trident') > -1) { const version = userAgent.match(/(MSIE |rv:)(\d+)/)[2]; if (version < 9) { alert(`检测到您使用的是IE${version},本系统需要IE9+或其他现代浏览器!`); document.body.innerHTML = ` <div style="text-align:center; padding:50px;"> <h1>亲爱的IE用户</h1> <img src="https://http.cat/418.jpg" alt="IE已淘汰"> <p>请使用Chrome/Firefox/Edge/Safari等现代浏览器</p> </div> `; } } } } }; .file-uploader { max-width: 800px; margin: 0 auto; padding: 20px; font-family: Arial, sans-serif; } .upload-area { margin: 20px 0; padding: 20px; border: 2px dashed #ccc; text-align: center; } .progress-container { margin: 20px 0; height: 30px; background: #f0f0f0; border-radius: 15px; position: relative; } .progress-bar { height: 100%; background: #4CAF50; border-radius: 15px; width: 0%; transition: width 0.3s; } .progress-text { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); color: #333; } .file-list { margin-top: 30px; } .file-list ul { list-style: none; padding: 0; } .file-list li { padding: 8px; border-bottom: 1px solid #eee; }

项目趣事

  1. IE8兼容大战:为了兼容IE8,我尝试了各种polyfill,最后发现还是直接显示"请升级浏览器"更实际

  2. 文件夹上传:WebUploader对文件夹上传支持不好,我研究了半天发现原来要用webkitdirectory属性

  3. 断点续传:本来想用IndexedDB存储上传进度,发现IE不支持,最后改用localStorage(虽然有大小限制)

  4. 加密传输:研究了AES加密,发现实现起来太复杂,最后用了简单的Base64编码(导师说这不算真正的加密)

求助与展望

现在前端部分勉强能跑,但后端完全没头绪。群里的小伙伴们也都在喊"后端大佬救命"!

在此我郑重声明:

  1. 不提供后端代码(因为我也不会)
  2. 欢迎大佬加入QQ群374992201指导
  3. 加群送1-99元红包(老板说这是营销策略)
  4. 推荐工作成功者送20%项目提成(虽然现在还没项目)

最后,如果哪位师哥师姐愿意收我为徒,教我后端开发,我愿意:

  • 每天给您请安
  • 帮您写前端代码
  • 毕业设计可以挂您名字
  • 未来第一年工资分您10%

(联系方式:QQ群374992201,群里找我"求带的小张")

将组件复制到项目中

示例中已经包含此目录

引入组件

配置接口地址

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

处理事件

启动测试

启动成功

效果

数据库

效果预览

文件上传

文件刷新续传

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

文件夹上传

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

下载示例

点击下载完整示例

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

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

相关文章

化妆品成分识别:扫描包装获取过敏原与功效说明

化妆品成分识别&#xff1a;扫描包装获取过敏原与功效说明 随着消费者对护肤品安全性和功效性的关注度持续提升&#xff0c;如何快速、准确地理解化妆品包装上的复杂成分表&#xff0c;成为日常选购中的关键痛点。尤其对于敏感肌人群&#xff0c;识别潜在过敏原&#xff08;如酒…

避免API调用限流:MGeo本地部署保障服务连续性

避免API调用限流&#xff1a;MGeo本地部署保障服务连续性 在地理信息处理、地址清洗与实体对齐等场景中&#xff0c;地址相似度匹配是构建高质量数据链路的核心环节。尤其在电商平台、物流系统和城市治理项目中&#xff0c;面对海量中文地址数据&#xff08;如“北京市朝阳区建…

盲盒一番无限赏小程序开发全解析:技术难点+落地指南

在潮玩数字化赛道中&#xff0c;盲盒一番赏凭借“分级惊喜IP溢价”&#xff0c;叠加无限赏“循环激励”机制&#xff0c;成为小程序开发新风口——头部IP联名款上线3日内峰值QPS突破5000&#xff0c;30日留存率达35%&#xff0c;远超普通盲盒产品。但多数开发者陷入高并发卡顿、…

电商系统API签名错误实战排查指南

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 开发一个电商API签名验证模拟系统&#xff0c;包含&#xff1a;1) 商品查询API 2) 订单创建API 3) 支付回调API。模拟签名错误的常见场景&#xff1a;时间戳过期、密钥错误、参数顺…

品牌舆情监控:从社交图片中识别自家产品曝光

品牌舆情监控&#xff1a;从社交图片中识别自家产品曝光 在社交媒体主导信息传播的今天&#xff0c;品牌方越来越依赖非结构化数据来感知市场动态。传统文本舆情分析已无法满足全面洞察需求——用户更倾向于通过图片分享消费体验。如何从海量社交图片中自动识别自家产品的“被动…

一站式解决方案:中文万物识别模型部署完全指南

一站式解决方案&#xff1a;中文万物识别模型部署完全指南 如果你正在寻找一个开箱即用的中文万物识别解决方案&#xff0c;但苦于缺乏专业的AI基础设施团队&#xff0c;这篇文章将为你提供一个从环境搭建到API部署的完整指南。通过预置的中文万物识别模型镜像&#xff0c;即使…

AI如何优化滑模控制算法?让系统更稳定高效

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 开发一个基于AI的滑模控制优化系统&#xff0c;要求&#xff1a;1. 使用Python实现滑模控制基础框架&#xff1b;2. 集成Kimi-K2模型预测系统扰动&#xff1b;3. 设计自适应滑模面…

从小白到专家:一站式OpenMMLab环境搭建秘籍

从小白到专家&#xff1a;一站式OpenMMLab环境搭建秘籍 如果你正在转行学习计算机视觉&#xff0c;可能会被各种框架和工具链搞得晕头转向。OpenMMLab作为计算机视觉领域的重要开源项目集合&#xff0c;包含了MMDetection、MMSegmentation、MMClassification等多个子项目&#…

热传导过程模拟验证:红外热像仪数据比对

热传导过程模拟验证&#xff1a;红外热像仪数据比对 引言&#xff1a;从物理仿真到真实世界的数据校验 在工程热力学与材料科学领域&#xff0c;热传导过程的数值模拟已成为产品设计、安全评估和能效优化的重要工具。然而&#xff0c;任何仿真模型的可信度最终都依赖于其与实…

告别繁琐!网络规划效率提升300%的秘诀

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 设计一个网络规划效率对比工具&#xff0c;可以并行展示传统手动规划流程和AI辅助规划的流程差异。包括时间消耗对比、方案质量评估、人工干预次数等关键指标的可视化展示&#xf…

零基础图解:SQL Server2022安装Step by Step

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 设计一个交互式SQL Server2022安装学习应用&#xff0c;包含&#xff1a;1.分步骤动画演示 2.实时操作验证 3.常见问题即时解答 4.安装模拟练习环境 5.进度保存功能。要求界面友好…

MGeo与Tableau集成:地理匹配结果可视化展示

MGeo与Tableau集成&#xff1a;地理匹配结果可视化展示 引言&#xff1a;从地址相似度识别到空间数据智能可视化 在城市计算、物流调度、零售选址等场景中&#xff0c;地址数据的标准化与实体对齐是构建高质量空间数据库的关键前提。然而&#xff0c;中文地址存在表述多样、缩写…

农业无人机航拍图像作物分布识别统计

农业无人机航拍图像作物分布识别统计 引言&#xff1a;从农田到算法——AI如何重塑现代农业管理 随着精准农业的快速发展&#xff0c;无人机航拍技术已成为农田监测的重要手段。通过高空视角获取高分辨率图像&#xff0c;农民和农技人员可以实时掌握作物生长状态、病虫害情况以…

30分钟构建UCRTBASED.DLL修复工具原型

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 快速开发一个UCRTBASED.DLL修复工具的最小可行产品(MVP)&#xff0c;包含核心功能&#xff1a;1) 基础检测功能 2) 简单修复选项 3) 状态反馈。要求使用Python编写控制台应用&…

有道翻译机效果下降?线上模型Hunyuan-MT-7B值得尝试

有道翻译机效果下降&#xff1f;线上模型Hunyuan-MT-7B值得尝试 在全球化日益深入的今天&#xff0c;跨语言沟通早已不再是科研机构或跨国企业的专属需求。从跨境电商的商品描述翻译&#xff0c;到少数民族地区的教育资料本地化&#xff0c;再到个人用户日常的外文阅读&#x…

瑜伽姿势识别纠正:智能镜子背后的算法逻辑

瑜伽姿势识别纠正&#xff1a;智能镜子背后的算法逻辑 引言&#xff1a;从万物识别到智能健身的跨越 在计算机视觉技术飞速发展的今天&#xff0c;"万物识别"已不再是科幻概念。阿里云近期开源的万物识别-中文-通用领域模型&#xff0c;标志着图像理解能力迈入了更…

AI助力LODOP开发:自动生成打印控件代码

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 创建一个基于LODOP的打印控件生成工具&#xff0c;能够根据用户输入的打印需求自动生成JavaScript代码。功能包括&#xff1a;1. 支持常见打印元素如文本、表格、条形码的配置 2. …

从零开始搭建地址匹配服务:MGeo+Jupyter Notebook实操教程

从零开始搭建地址匹配服务&#xff1a;MGeoJupyter Notebook实操教程 学习目标与背景介绍 在电商、物流、城市治理等实际业务场景中&#xff0c;地址数据的标准化与匹配是数据清洗和实体对齐的关键环节。由于中文地址存在表述多样、缩写习惯不一、层级嵌套复杂等问题&#xf…

MGeo模型负载测试:千级QPS压力表现如何?

MGeo模型负载测试&#xff1a;千级QPS压力表现如何&#xff1f; 背景与挑战&#xff1a;中文地址相似度匹配的工程化瓶颈 在电商、物流、本地生活等业务场景中&#xff0c;地址数据的标准化与实体对齐是数据清洗和用户画像构建的关键环节。由于中文地址存在大量别名、缩写、语序…

10款机器学习镜像测评:MGeo在中文地址领域表现突出

10款机器学习镜像测评&#xff1a;MGeo在中文地址领域表现突出 背景与选型动因 在电商、物流、金融风控等实际业务场景中&#xff0c;地址信息的标准化与实体对齐是数据清洗和用户画像构建的关键环节。然而&#xff0c;中文地址具有高度非结构化、缩写多样、语序灵活等特点&…