news 2026/4/3 5:45:44

网页前端如何配合JSP完成1T文件分块上传?

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
网页前端如何配合JSP完成1T文件分块上传?

大文件上传系统开发指南(兼容IE8的WebUploader实现)

项目概述

大家好,我是广东的一名.NET程序员,最近接了一个让人头大的外包项目。客户要求实现一个支持20G大文件上传的系统,还要兼容IE8这种古董浏览器,预算只有100元!不过没关系,咱们程序员最擅长的就是在有限条件下创造无限可能。

技术选型分析

经过深思熟虑,我决定采用以下技术方案:

  1. 前端:WebUploader(兼容IE8) + 原生JavaScript(因为客户要求不能用jQuery等库)
  2. 后端:ASP.NET WebForm(客户指定的技术栈)
  3. 存储:阿里云OSS(私有云存储)
  4. 数据库:SQL Server(记录文件元数据)

系统功能清单

  • 大文件上传(支持20G)
  • 文件夹上传(保留层级结构)
  • 文件下载(非打包方式)
  • 加密传输(SM4/AES)
  • 断点续传(网页关闭后恢复)
  • 兼容IE8及所有主流浏览器
  • 文件夹下载(保留结构)

前端实现代码

HTML部分 (index.html)

大文件上传系统 - 兼容IE8 body { font-family: Arial, sans-serif; margin: 20px; } .uploader-container { margin: 20px 0; } .file-list { margin-top: 20px; border: 1px solid #ddd; padding: 10px; } .progress { height: 20px; margin: 5px 0; } .ie-warning { color: red; background: #ffeeee; padding: 10px; display: none; } 大文件上传系统 您正在使用旧版浏览器,部分功能可能受限,建议升级到现代浏览器。 选择文件/文件夹 开始上传

JavaScript部分 (app.js)

// 检测IE8并显示警告functioncheckIE(){varua=window.navigator.userAgent;varmsie=ua.indexOf('MSIE ');if(msie>0||!!navigator.userAgent.match(/Trident.*rv:11\./)){varversion=parseInt(ua.substring(msie+5,ua.indexOf('.',msie)));if(version<=8){document.getElementById('ie-warning').style.display='block';}}}// 主上传逻辑(function(){checkIE();// 配置项varconfig={chunkSize:5*1024*1024,// 5MB分片threads:3,// 并发数server:'/FileUpload.ashx',// 上传处理地址encryptKey:'12345678901234567890123456789012'// AES加密密钥(32位)};// 初始化WebUploadervaruploader=WebUploader.create({swf:'//cdn.jsdelivr.net/npm/webuploader@0.1.1/dist/Uploader.swf',server:config.server,pick:'#filePicker',resize:false,chunked:true,chunkSize:config.chunkSize,threads:config.threads,formData:{// 可以在这里添加额外的表单数据},compress:false,duplicate:true,accept:null,// 允许文件夹上传(通过webkitdirectory属性)directory:true});// 文件加入队列事件uploader.on('fileQueued',function(file){// 如果是文件夹,记录相对路径if(file._relativePath){file.relativePath=file._relativePath;}// 创建文件项UIvar$list=document.getElementById('fileList');var$item=document.createElement('div');$item.id=file.id;$item.innerHTML=''+' '+file.name+''+' ('+WebUploader.formatSize(file.size)+')'+' 等待上传...'+' '+'';$list.appendChild($item);});// 上传进度事件uploader.on('uploadProgress',function(file,percentage){var$item=document.getElementById(file.id);var$progress=$item.querySelector('.progress');$progress.style.width=(percentage*100)+'%';var$status=$item.querySelector('.filestatus');$status.innerHTML='上传中 '+Math.round(percentage*100)+'%';});// 上传成功事件uploader.on('uploadSuccess',function(file,response){var$item=document.getElementById(file.id);var$status=$item.querySelector('.filestatus');if(response.success){$status.innerHTML='上传成功';$item.querySelector('.progress').style.background='#5cb85c';}else{$status.innerHTML='上传失败: '+response.message;$item.querySelector('.progress').style.background='#d9534f';}});// 上传错误事件uploader.on('uploadError',function(file,reason){var$item=document.getElementById(file.id);var$status=$item.querySelector('.filestatus');$status.innerHTML='上传错误: '+reason;$item.querySelector('.progress').style.background='#d9534f';});// 开始上传按钮document.getElementById('ctlBtn').addEventListener('click',function(){uploader.upload();});// 加密函数 (AES)functionencryptData(data){// 在实际应用中,应该使用更安全的密钥管理方式varkey=CryptoJS.enc.Utf8.parse(config.encryptKey);variv=CryptoJS.enc.Utf8.parse(config.encryptKey.substring(0,16));// 如果是文件对象,我们实际上是在上传时对分片进行加密// 这里演示对字符串加密if(typeofdata==='string'){varencrypted=CryptoJS.AES.encrypt(data,key,{iv:iv,mode:CryptoJS.mode.CBC,padding:CryptoJS.pad.Pkcs7});returnencrypted.toString();}returndata;}// 在上传前对文件分片进行加密(简化版)// 实际项目中,应该在服务端进行加密,这里只是演示uploader.on('uploadBeforeSend',function(block,data){// 可以在这里对block.blob进行加密处理// 但由于浏览器端加密大文件性能较差,建议仅作演示// 实际项目中应该使用HTTPS + 服务端加密});// 断点续传支持// 通过WebUploader的chunked和uniqueIdentifier实现// 服务端需要配合记录上传进度// 文件夹下载功能将在服务端实现})();

后端实现代码 (ASP.NET WebForm)

文件上传处理 (FileUpload.ashx)

<%@WebHandlerLanguage="C#"Class="FileUpload"%>usingSystem;usingSystem.Web;usingSystem.IO;usingSystem.Data.SqlClient;usingSystem.Security.Cryptography;usingSystem.Text;usingAliyun.OSS;publicclassFileUpload:IHttpHandler{// 配置信息(实际项目中应该放在配置文件中)privatestaticreadonlystringOssEndpoint="your-oss-endpoint";privatestaticreadonlystringOssAccessKeyId="your-access-key-id";privatestaticreadonlystringOssAccessKeySecret="your-access-key-secret";privatestaticreadonlystringOssBucketName="your-bucket-name";privatestaticreadonlystringConnString="your-sqlserver-connection-string";publicvoidProcessRequest(HttpContextcontext){context.Response.ContentType="application/json";try{// 检查请求方法if(context.Request.HttpMethod!="POST"){context.Response.Write("{\"success\":false,\"message\":\"只支持POST请求\"}");return;}// 获取上传参数varfile=context.Request.Files["file"];varchunk=context.Request["chunk"]!=null?int.Parse(context.Request["chunk"]):0;varchunks=context.Request["chunks"]!=null?int.Parse(context.Request["chunks"]):1;varfileId=context.Request["fileId"]??Guid.NewGuid().ToString();varfileName=context.Request["name"]??file.FileName;varrelativePath=context.Request["relativePath"]??"";varfileSize=context.Request["totalSize"]!=null?long.Parse(context.Request["totalSize"]):file.ContentLength;// 创建OSS客户端varclient=newOssClient(OssEndpoint,OssAccessKeyId,OssAccessKeySecret);// 临时文件路径(分片存储)vartempFolder=Path.Combine(context.Server.MapPath("~/App_Data/UploadTemp"),fileId);if(!Directory.Exists(tempFolder)){Directory.CreateDirectory(tempFolder);}vartempFilePath=Path.Combine(tempFolder,chunk.ToString());// 保存分片using(varfs=newFileStream(tempFilePath,FileMode.Create)){// 这里可以添加解密逻辑(如果前端加密了)// 实际项目中,建议使用HTTPS + 服务端加密file.InputStream.CopyTo(fs);}// 如果是最后一个分片,合并文件if(chunk==chunks-1){varfinalFilePath=Path.Combine(context.Server.MapPath("~/App_Data/UploadFiles"),Guid.NewGuid().ToString()+Path.GetExtension(fileName));// 合并文件MergeFile(tempFolder,finalFilePath,chunks);// 上传到OSSvarossKey=Path.Combine(DateTime.Now.ToString("yyyyMMdd"),Guid.NewGuid().ToString()+Path.GetExtension(fileName));using(varfs=newFileStream(finalFilePath,FileMode.Open)){// 这里可以添加加密逻辑(使用SM4或AES)// 示例:使用AES加密(实际项目中应该使用更安全的密钥管理)varencryptedData=EncryptStream(fs,"your-encryption-key-32-chars-long");// 重置流位置encryptedData.Position=0;// 上传到OSSclient.PutObject(OssBucketName,ossKey,encryptedData);}// 删除临时文件Directory.Delete(tempFolder,true);File.Delete(finalFilePath);// 记录文件元数据到数据库RecordFileMetadata(fileName,relativePath,fileSize,ossKey);context.Response.Write(string.Format("{{\"success\":true,\"fileId\":\"{0}\",\"ossKey\":\"{1}\"}}",fileId,ossKey));}else{context.Response.Write(string.Format("{{\"success\":true,\"fileId\":\"{0}\",\"chunk\":{1}}}",fileId,chunk));}}catch(Exceptionex){context.Response.Write(string.Format("{{\"success\":false,\"message\":\"{0}\"}}",ex.Message.Replace("\"","\\\"")));}}// 合并分片文件privatevoidMergeFile(stringtempFolder,stringfinalFilePath,intchunks){using(varfs=newFileStream(finalFilePath,FileMode.Create)){for(vari=0;i<chunks;i++){varchunkPath=Path.Combine(tempFolder,i.ToString());using(varchunkStream=newFileStream(chunkPath,FileMode.Open)){chunkStream.CopyTo(fs);}File.Delete(chunkPath);}}}// 加密流(简化版,实际项目中使用更安全的实现)privateMemoryStreamEncryptStream(Streaminput,stringkey){// 读取整个流到内存(仅用于演示,大文件应该使用流式加密)using(varms=newMemoryStream()){input.CopyTo(ms);vardata=ms.ToArray();// 使用AES加密using(varaes=Aes.Create()){aes.Key=Encoding.UTF8.GetBytes(key.PadRight(32).Substring(0,32));aes.IV=Encoding.UTF8.GetBytes(key.PadRight(16).Substring(0,16));using(varencryptor=aes.CreateEncryptor())using(varresultStream=newMemoryStream()){using(varcryptoStream=newCryptoStream(resultStream,encryptor,CryptoStreamMode.Write)){cryptoStream.Write(data,0,data.Length);}returnresultStream;}}}}// 记录文件元数据到数据库privatevoidRecordFileMetadata(stringfileName,stringrelativePath,longsize,stringossKey){using(varconn=newSqlConnection(ConnString)){conn.Open();varcmd=newSqlCommand("INSERT INTO FileMetadata (FileName, RelativePath, FileSize, OssKey, UploadTime) "+"VALUES (@FileName, @RelativePath, @FileSize, @OssKey, @UploadTime)",conn);cmd.Parameters.AddWithValue("@FileName",fileName);cmd.Parameters.AddWithValue("@RelativePath",relativePath);cmd.Parameters.AddWithValue("@FileSize",size);cmd.Parameters.AddWithValue("@OssKey",ossKey);cmd.Parameters.AddWithValue("@UploadTime",DateTime.Now);cmd.ExecuteNonQuery();}}publicboolIsReusable{get{returnfalse;}}}

文件下载处理 (FileDownload.ashx)

<%@WebHandlerLanguage="C#"Class="FileDownload"%>usingSystem;usingSystem.Web;usingSystem.IO;usingSystem.Data.SqlClient;usingSystem.Security.Cryptography;usingSystem.Text;usingAliyun.OSS;publicclassFileDownload:IHttpHandler{// 配置信息(同上传处理)privatestaticreadonlystringOssEndpoint="your-oss-endpoint";privatestaticreadonlystringOssAccessKeyId="your-access-key-id";privatestaticreadonlystringOssAccessKeySecret="your-access-key-secret";privatestaticreadonlystringOssBucketName="your-bucket-name";privatestaticreadonlystringConnString="your-sqlserver-connection-string";publicvoidProcessRequest(HttpContextcontext){try{varossKey=context.Request["ossKey"];varisFolder=context.Request["isFolder"]=="true";if(string.IsNullOrEmpty(ossKey)){thrownewException("缺少ossKey参数");}// 创建OSS客户端varclient=newOssClient(OssEndpoint,OssAccessKeyId,OssAccessKeySecret);if(isFolder){// 文件夹下载 - 查询所有子文件varfileList=GetFolderFiles(ossKey);// 设置响应头(实际项目中应该使用ZIP打包下载,但客户要求非打包方式)// 这里改为返回文件列表,由前端逐个下载context.Response.ContentType="application/json";context.Response.Write(Newtonsoft.Json.JsonConvert.SerializeObject(fileList));}else{// 单个文件下载varfileMeta=GetFileMetadata(ossKey);if(fileMeta==null){thrownewException("文件不存在");}// 从OSS获取文件对象varobj=client.GetObject(OssBucketName,ossKey);// 设置响应头context.Response.ContentType="application/octet-stream";context.Response.AddHeader("Content-Disposition","attachment; filename="+HttpUtility.UrlEncode(fileMeta.FileName));// 解密并输出文件内容using(varossStream=obj.Content){// 这里可以添加解密逻辑(与上传时的加密对应)// 示例:使用AES解密vardecryptedStream=DecryptStream(ossStream,"your-encryption-key-32-chars-long");// 输出到响应流decryptedStream.CopyTo(context.Response.OutputStream);}}}catch(Exceptionex){context.Response.ContentType="application/json";context.Response.Write(string.Format("{{\"success\":false,\"message\":\"{0}\"}}",ex.Message.Replace("\"","\\\"")));}}// 解密流(与上传时的加密对应)privateStreamDecryptStream(Streaminput,stringkey){// 读取整个流到内存(仅用于演示,大文件应该使用流式解密)using(varms=newMemoryStream()){input.CopyTo(ms);vardata=ms.ToArray();// 使用AES解密using(varaes=Aes.Create()){aes.Key=Encoding.UTF8.GetBytes(key.PadRight(32).Substring(0,32));aes.IV=Encoding.UTF8.GetBytes(key.PadRight(16).Substring(0,16));using(vardecryptor=aes.CreateDecryptor())using(varresultStream=newMemoryStream()){using(varcryptoStream=newCryptoStream(resultStream,decryptor,CryptoStreamMode.Write)){cryptoStream.Write(data,0,data.Length);}returnresultStream;}}}}// 获取文件夹下的所有文件privateobjectGetFolderFiles(stringfolderPath){// 实际项目中应该查询数据库获取文件夹结构// 这里简化处理,返回模拟数据returnnew{success=true,files=new[]{new{name="file1.txt",size=1024,ossKey="path/to/file1.txt"},new{name="subfolder/file2.txt",size=2048,ossKey="path/to/subfolder/file2.txt"}}};}// 获取文件元数据privatedynamicGetFileMetadata(stringossKey){using(varconn=newSqlConnection(ConnString)){conn.Open();varcmd=newSqlCommand("SELECT FileName, RelativePath, FileSize FROM FileMetadata WHERE OssKey = @OssKey",conn);cmd.Parameters.AddWithValue("@OssKey",ossKey);using(varreader=cmd.ExecuteReader()){if(reader.Read()){returnnew{FileName=reader["FileName"].ToString(),RelativePath=reader["RelativePath"].ToString(),FileSize=Convert.ToInt64(reader["FileSize"])};}}}returnnull;}publicboolIsReusable{get{returnfalse;}}}

数据库表设计

-- 文件元数据表CREATETABLEFileMetadata(IdINTIDENTITY(1,1)PRIMARYKEY,FileName NVARCHAR(255)NOTNULL,RelativePath NVARCHAR(MAX)NULL,FileSizeBIGINTNOTNULL,OssKey NVARCHAR(500)NOTNULL,UploadTimeDATETIMENOTNULL,IsDeletedBITDEFAULT0,DeleteTimeDATETIMENULL);-- 上传进度记录表(用于断点续传)CREATETABLEUploadProgress(IdINTIDENTITY(1,1)PRIMARYKEY,FileId NVARCHAR(50)NOTNULL,FileName NVARCHAR(255)NOTNULL,TotalSizeBIGINTNOTNULL,UploadedSizeBIGINTNOTNULL,ChunkCountINTNOTNULL,UploadedChunks NVARCHAR(MAX)NULL,-- JSON格式存储已上传的分片索引LastUpdateDATETIMENOTNULL,IsCompletedBITDEFAULT0);

项目部署说明

  1. 环境准备

    • Windows Server + IIS
    • .NET Framework 4.5+
    • SQL Server 2012+
    • 阿里云OSS账号
  2. 部署步骤

    • 在IIS中创建网站,指向项目目录
    • 配置应用程序池为.NET 4.0
    • 修改Web.config中的数据库连接字符串
    • 配置OSS访问密钥
    • 确保App_Data目录有写入权限
  3. 兼容性测试

    • 在IE8及其他浏览器中测试上传下载功能
    • 测试大文件(>5GB)上传
    • 测试文件夹上传(包含多级子目录)

注意事项

  1. 加密实现

    • 示例中的加密是简化版,实际项目应该:
      • 使用HTTPS传输
      • 服务端使用更安全的密钥管理
      • 考虑使用国密SM4算法(需要引入相关库)
  2. 性能优化

    • 对于超大文件,考虑使用流式加密而非内存加密
    • 可以使用异步处理提高服务器响应能力
  3. 断点续传

    • 示例中简化了实现,实际项目应该:
      • 在数据库中记录上传进度
      • 处理各种异常情况(网络中断、服务器重启等)
  4. 文件夹下载

    • 客户要求非打包下载,实际实现中:
      • 可以返回文件列表由前端逐个下载
      • 或者使用服务端生成临时链接的方式

总结

这个项目虽然预算有限(只有100元),但通过合理的技术选型和代码实现,我们还是能够满足客户的需求。关键点在于:

  1. 使用WebUploader实现兼容IE8的文件上传
  2. 通过分片上传支持大文件
  3. 利用OSS存储解决服务器空间问题
  4. 数据库记录元数据支持断点续传

虽然代码中有些简化处理(如加密),但整体架构是完整的,你可以根据实际需求进行扩展。

最后,欢迎加入我们的QQ群(374992201)交流技术,一起接单赚钱!群里经常有红包和项目分享,说不定下一个10万项目就在群里等着你呢!

导入项目

导入到Eclipse:点南查看教程
导入到IDEA:点击查看教程
springboot统一配置:点击查看教程

工程

NOSQL

NOSQL示例不需要任何配置,可以直接访问测试

创建数据表

选择对应的数据表脚本,这里以SQL为例

修改数据库连接信息

访问页面进行测试

文件存储路径

up6/upload/年/月/日/guid/filename

效果预览

文件上传

文件刷新续传

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

文件夹上传

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

下载示例

点击下载完整示例

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

手把手教你学Simulink——移动机器人基础驱动场景实例:基于Simulink的无刷直流电机(BLDC)方波驱动轮毂控制仿真

目录 手把手教你学Simulink——移动机器人基础驱动场景实例&#xff1a;基于Simulink的无刷直流电机&#xff08;BLDC&#xff09;方波驱动轮毂控制仿真 一、引言&#xff1a;简单、高效、可靠——BLDC方波驱动是移动机器人的经济之选 二、BLDC方波驱动原理 1. 电机结构特点…

作者头像 李华
网站建设 2026/3/15 12:12:08

音频AI终极突破:全新架构如何重塑智能交互范式

在人工智能技术快速演进的当下&#xff0c;音频AI技术正迎来关键转折点。作为智能交互的核心入口&#xff0c;新一代多模态音频理解系统正在突破传统语音识别的局限&#xff0c;构建从信号感知到场景认知的完整技术栈。本文将深度解析这一技术突破如何重新定义人机交互的未来图…

作者头像 李华
网站建设 2026/3/23 10:35:30

三步掌握化学核心:高清中文元素周期表使用全攻略 [特殊字符]

三步掌握化学核心&#xff1a;高清中文元素周期表使用全攻略 &#x1f9ea; 【免费下载链接】元素周期表高清中文版最新分享 元素周期表高清中文版最新 项目地址: https://gitcode.com/Open-source-documentation-tutorial/c6295 还在为寻找高质量的元素周期表资源而烦恼…

作者头像 李华
网站建设 2026/3/30 18:48:41

Triton多端口流量监控终极指南:从零构建全方位观测体系

当Triton推理服务器在生产环境中遭遇性能瓶颈时&#xff0c;传统的单点监控往往难以定位多端口流量异常。本文将通过环形结构设计&#xff0c;带您从实际问题出发&#xff0c;逐步构建覆盖HTTP、gRPC和Metrics端口的完整监控方案&#xff0c;让您真正掌握Triton多端口流量监控的…

作者头像 李华
网站建设 2026/4/2 20:27:16

12、无线传感器节点与PC通信技术解析

无线传感器节点与PC通信技术解析 1. 日志存储与操作 日志存储有着独特的特性,其实现保证了单次 LogWrite.append 操作写入的数据要么完整存在,要么完全不存在,所以日志中不会包含部分摘要。我们可以从日志开头以512字节(即一个摘要的大小)为单位进行读取,以此获取有效…

作者头像 李华