当前位置: 首页 > news >正文

JQuery如何通过控件实现多平台兼容的断点续传功能?

大文件上传解决方案

作为一个同样接外包接到手软的.NET老油条,我完全理解你既要兼容IE8又要20G文件上传还要100块预算的"无理要求"。来,给你整一套"饿不死套餐"!

前端实现 - 兼容IE8的土味方案

穷逼版大文件上传 /* 祖传CSS,兼容IE8 */ .upload-area { border: 2px dashed #ccc; padding: 20px; text-align: center; margin: 20px; } .progress-container { width: 100%; background-color: #f5f5f5; margin: 10px 0; } .progress-bar { height: 20px; background-color: #4CAF50; width: 0%; } .file-list { margin: 20px; border: 1px solid #ddd; padding: 10px; } 大文件上传(兼容IE8版) 拖放文件或文件夹到此处 或 选择文件/文件夹 等待上传文件... 开始上传 暂停 继续 加密方式: SM4国密 AES // 兼容IE8的JSON if (typeof JSON === "undefined") { document.write('<scriptsrc="https://cdnjs.cloudflare.com/ajax/libs/json2/20150503/json2.min.js"><\/script>'); } // 上传队列 var uploadQueue = []; var currentUpload = null; var chunkSize = 5 * 1024 * 1024; // 5MB分块 // 文件选择处理 document.getElementById('fileInput').addEventListener('change', function(e) { handleFiles(e.target.files); }); // 拖放处理 var dropArea = document.getElementById('dropArea'); dropArea.addEventListener('dragover', function(e) { e.preventDefault(); dropArea.style.borderColor = '#4CAF50'; }); dropArea.addEventListener('dragleave', function() { dropArea.style.borderColor = '#ccc'; }); dropArea.addEventListener('drop', function(e) { e.preventDefault(); dropArea.style.borderColor = '#ccc'; handleFiles(e.dataTransfer.files); }); // 处理文件选择 function handleFiles(files) { var fileList = document.getElementById('fileList'); fileList.innerHTML = ''; for (var i = 0; i < files.length; i++) { var file = files[i]; var fileItem = document.createElement('div'); fileItem.innerHTML = file.name + ' (' + formatFileSize(file.size) + ')'; fileList.appendChild(fileItem); uploadQueue.push({ file: file, progress: 0, status: 'pending' }); } } // 开始上传 function startUpload() { if (uploadQueue.length === 0) return; currentUpload = uploadQueue[0]; uploadFile(currentUpload); } // 上传文件 function uploadFile(fileItem) { var file = fileItem.file; var fileId = generateFileId(file); var totalChunks = Math.ceil(file.size / chunkSize); // 从本地存储加载断点 var resumeChunk = localStorage.getItem('resume_' + fileId) || 0; // 上传分块 for (var chunkIndex = resumeChunk; chunkIndex < totalChunks; chunkIndex++) { if (fileItem.status === 'paused') break; var start = chunkIndex * chunkSize; var end = Math.min(start + chunkSize, file.size); var chunk = file.slice(start, end); // 加密分块(伪代码) var encryptedChunk = encryptChunk( chunk, document.getElementById('encryptionType').value, document.getElementById('encryptionKey').value ); var formData = new FormData(); formData.append('fileId', fileId); formData.append('chunkIndex', chunkIndex); formData.append('totalChunks', totalChunks); formData.append('fileName', file.name); formData.append('filePath', file.webkitRelativePath || ''); formData.append('fileSize', file.size); formData.append('chunkData', encryptedChunk); formData.append('encryption', document.getElementById('encryptionType').value); // AJAX上传(兼容IE8) var xhr = new XMLHttpRequest(); xhr.open('POST', '/api/upload/chunk', false); // 同步上传,IE8不支持Promise xhr.upload.onprogress = function(e) { var loaded = chunkIndex * chunkSize + e.loaded; fileItem.progress = Math.round((loaded / file.size) * 100); updateProgress(); }; xhr.onreadystatechange = function() { if (xhr.readyState === 4) { if (xhr.status === 200) { // 保存断点 localStorage.setItem('resume_' + fileId, chunkIndex + 1); if (chunkIndex === totalChunks - 1) { // 合并文件 mergeFile(fileId); fileItem.status = 'completed'; uploadQueue.shift(); startUpload(); } } else { console.error('上传失败:', xhr.responseText); } } }; xhr.send(formData); } } // 暂停上传 function pauseUpload() { if (currentUpload) { currentUpload.status = 'paused'; } } // 继续上传 function resumeUpload() { if (currentUpload && currentUpload.status === 'paused') { currentUpload.status = 'uploading'; uploadFile(currentUpload); } } // 合并文件 function mergeFile(fileId) { var xhr = new XMLHttpRequest(); xhr.open('POST', '/api/upload/merge', false); xhr.setRequestHeader('Content-Type', 'application/json'); xhr.onreadystatechange = function() { if (xhr.readyState === 4 && xhr.status === 200) { console.log('文件合并成功:', xhr.responseText); localStorage.removeItem('resume_' + fileId); } }; xhr.send(JSON.stringify({ fileId: fileId, encryption: document.getElementById('encryptionType').value })); } // 生成文件ID function generateFileId(file) { return file.name + '_' + file.size + '_' + file.lastModifiedDate.getTime(); } // 加密分块(伪实现) function encryptChunk(chunk, algorithm, key) { // 实际项目中应该使用Web Crypto API或相应库 console.log('使用' + algorithm + '加密分块,密钥:', key); return chunk; } // 更新进度显示 function updateProgress() { var progressBars = document.querySelectorAll('.progress-bar'); for (var i = 0; i < progressBars.length; i++) { progressBars[i].style.width = uploadQueue[i].progress + '%'; } } // 格式化文件大小 function formatFileSize(bytes) { if (bytes === 0) return '0 B'; var k = 1024; var sizes = ['B', 'KB', 'MB', 'GB', 'TB']; var i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; }

后端实现 - ASP.NET WebForm版

FileUploadHandler.ashx

<%@WebHandlerLanguage="C#"Class="FileUploadHandler"%>usingSystem;usingSystem.Web;usingSystem.IO;usingSystem.Collections.Generic;usingSystem.Linq;publicclassFileUploadHandler:IHttpHandler{publicvoidProcessRequest(HttpContextcontext){context.Response.ContentType="application/json";try{stringaction=context.Request["action"];switch(action){case"upload_chunk":HandleChunkUpload(context);break;case"merge_file":HandleMergeFile(context);break;default:context.Response.Write("{\"error\":\"无效操作\"}");break;}}catch(Exceptionex){context.Response.Write("{\"error\":\""+ex.Message+"\"}");}}privatevoidHandleChunkUpload(HttpContextcontext){stringfileId=context.Request["fileId"];intchunkIndex=int.Parse(context.Request["chunkIndex"]);inttotalChunks=int.Parse(context.Request["totalChunks"]);stringfileName=context.Request["fileName"];stringfilePath=context.Request["filePath"];longfileSize=long.Parse(context.Request["fileSize"]);stringencryption=context.Request["encryption"];HttpPostedFilechunkData=context.Request.Files["chunkData"];// 保存分块stringchunkDir=GetChunkDir(fileId);if(!Directory.Exists(chunkDir)){Directory.CreateDirectory(chunkDir);}stringchunkPath=Path.Combine(chunkDir,chunkIndex.ToString()+".chunk");chunkData.SaveAs(chunkPath);// 保存断点信息SaveResumeInfo(fileId,chunkIndex+1,totalChunks);context.Response.Write("{\"success\":true}");}privatevoidHandleMergeFile(HttpContextcontext){stringfileId=context.Request["fileId"];stringencryption=context.Request["encryption"];// 获取分块信息varresumeInfo=GetResumeInfo(fileId);if(resumeInfo==null){thrownewException("找不到文件断点信息");}stringchunkDir=GetChunkDir(fileId);stringfileName=Path.GetFileNameWithoutExtension(resumeInfo.FileName);stringfileExt=Path.GetExtension(resumeInfo.FileName);// 创建目标文件stringsaveDir=Path.Combine(AppDomain.CurrentDomain.BaseDirectory,"Uploads");if(!string.IsNullOrEmpty(resumeInfo.FilePath)){saveDir=Path.Combine(saveDir,resumeInfo.FilePath);}if(!Directory.Exists(saveDir)){Directory.CreateDirectory(saveDir);}stringsavePath=Path.Combine(saveDir,fileName+fileExt);using(FileStreamfs=newFileStream(savePath,FileMode.Create)){// 合并所有分块for(inti=0;i<resumeInfo.TotalChunks;i++){stringchunkPath=Path.Combine(chunkDir,i+".chunk");byte[]chunkData=File.ReadAllBytes(chunkPath);// 解密数据(伪实现)byte[]decryptedData=DecryptChunk(chunkData,encryption);fs.Write(decryptedData,0,decryptedData.Length);// 删除临时分块File.Delete(chunkPath);}}// 删除分块目录Directory.Delete(chunkDir);// 清理断点信息ClearResumeInfo(fileId);context.Response.Write("{\"success\":true, \"filePath\":\""+savePath.Replace("\\","/")+"\"}");}privatestringGetChunkDir(stringfileId){returnPath.Combine(AppDomain.CurrentDomain.BaseDirectory,"Temp",fileId);}privatevoidSaveResumeInfo(stringfileId,intchunkIndex,inttotalChunks){varinfo=newResumeInfo{FileId=fileId,ChunkIndex=chunkIndex,TotalChunks=totalChunks,LastUpdate=DateTime.Now};stringinfoPath=Path.Combine(GetChunkDir(fileId),"resume.info");File.WriteAllText(infoPath,Newtonsoft.Json.JsonConvert.SerializeObject(info));}privateResumeInfoGetResumeInfo(stringfileId){stringinfoPath=Path.Combine(GetChunkDir(fileId),"resume.info");if(File.Exists(infoPath)){returnNewtonsoft.Json.JsonConvert.DeserializeObject(File.ReadAllText(infoPath));}returnnull;}privatevoidClearResumeInfo(stringfileId){stringinfoPath=Path.Combine(GetChunkDir(fileId),"resume.info");if(File.Exists(infoPath)){File.Delete(infoPath);}}privatebyte[]DecryptChunk(byte[]encryptedData,stringalgorithm){// 实际项目中应该使用SM4/AES解密returnencryptedData;}publicboolIsReusable{get{returntrue;}}privateclassResumeInfo{publicstringFileId{get;set;}publicstringFileName{get;set;}publicstringFilePath{get;set;}publicintChunkIndex{get;set;}publicintTotalChunks{get;set;}publicDateTimeLastUpdate{get;set;}}}

数据库设计 - 乞丐版

CREATETABLE[dbo].[FileUploadRecords](50NOTNULL,255NOTNULL,512NULL,[FileSize][bigint]NOTNULL,512NOTNULL,20NULL,20NULL,50NULL,[UploadTime][datetime]NOTNULL,[CompleteTime][datetime]NULL,CONSTRAINT[PK_FileUploadRecords]PRIMARYKEYCLUSTERED([Id]ASC))

部署指南 - 手把手教学

  1. 前端部署

    • 把HTML文件扔到IIS网站目录
    • 确保启用静态内容
  2. 后端部署

    • 在Visual Studio中发布WebForm项目
    • 部署到IIS,确保应用程序池使用.NET 4.0
  3. 权限设置

    • 给Uploads和Temp目录添加IIS_IUSRS写权限
  4. IE8兼容性

    • 在IIS中添加MIME类型:.json application/json
    • 在页面添加X-UA-Compatible标签:

注意事项

  1. 加密部分:实际项目中应该使用正规加密库,这里只是伪实现

  2. 性能问题:IE8下同步上传会阻塞页面,用户体验极差,但甲方爸爸要兼容没办法

  3. 安全性:记得在前端和后端都做文件类型检查,防止上传恶意文件

  4. 预算问题:100块真的做不了什么,建议跟甲方商量加点钱

最后忠告

  1. 下次接单前先谈好兼容性要求
  2. IE8真的该淘汰了,尽量说服客户升级
  3. 100块的项目别指望7x24支持,除非你想饿死
  4. 加群374992201,里面有大佬可以教你如何合理报价

设置框架

安装.NET Framework 4.7.2
https://dotnet.microsoft.com/en-us/download/dotnet-framework/net472
框架选择4.7.2

添加3rd引用

编译项目

NOSQL

NOSQL无需任何配置可直接访问页面进行测试

SQL

使用IIS
大文件上传测试推荐使用IIS以获取更高性能。

使用IIS Express

小文件上传测试可以使用IIS Express

创建数据库

配置数据库连接信息

检查数据库配置

访问页面进行测试


相关参考:
文件保存位置,

效果预览

文件上传

文件刷新续传

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

文件夹上传

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

下载完整示例

下载完整示例

http://www.cnnetsun.cn/news/115460.html

相关文章:

  • 基于增量动力分析方法IDA求解易损性曲线的Matlab代码探秘
  • mysql面试题整理
  • 瞄准科技特长生!3 大核心编程考级赛事(CTL/YCL/GESP)深度对比
  • day38打卡
  • JavaEE进阶——SpringBoot日志从入门到精通
  • 结构体简单题
  • 时间序列回归预测:LSTM、CNN - LSTM、PSO - CNN - LSTM、GAPSO - CNN - LSTM大比拼
  • 飞轮储能系统的建模与 MATLAB 仿真:永磁同步电机作为飞轮驱动电机
  • 车间进度总卡壳?生产小工单的3个必备功能,90%企业都用错了
  • 如何用 ShedLock 让 Spring Boot 的定时任务在多实例环境下只执行一次
  • 基于MPC的永磁同步电机非线性终端滑模控制仿真研究
  • ISSA - CNN - BiLSTM多输入单输出回归的Python实现与改进
  • Q学习(Q-learning)路径规划算法实战
  • ANSYS/LS - dyna防爆涂层砂浆砖框架结构爆破荷载损伤响应案例探索
  • 基于TOA/FOA的无源定位方法MATLAB仿真探索
  • 基于一致性算法改进的自适应虚拟阻抗控制:解决双机并联功率分布不均
  • springboot框架对接物联网,配置TCP协议依赖,与设备通信,让TCP变的如此简单
  • 微软和布朗大学最新发现:让AI助手拥有18000多种技能的革命性突破
  • MATLAB仿真:二维TOA传感器网络定位与时钟偏差拟合,最小二乘求解
  • 【参数辨识】基于卡尔曼滤波(KF)估计离散线性系统对垂直起降(VTOL)飞行器的鲁棒辨识附matlab代码
  • 桥梁与隧道安全守护者 抗冰冻型风速监测方案
  • 05-FreeRTOS的内存管理
  • 基于改进蛇优化算法(GOSO/ISO)优化随机森林数据回归预测模型(含初始化种群混沌映射、减法...
  • 基于大数据的人脸识别系统设计与实现开题报告
  • 车载 Android 系统稳定性问题全解析:从性能到黑屏的排查指南
  • 气象在线监测系统助力智慧环境管理,金叶仪器专业气象监测解决方案
  • 【TVM 教程】交叉编译与 RPC
  • 腾讯云国际站代理商的QAPM服务能提供哪些专属服务?
  • 网安副业怎么选?漏洞挖掘、技术博客、竞赛奖金实战,哪个更适配你?
  • 量子计算验证方法:软件测试从业者的转型指南