news 2026/4/1 3:47:34

银行系统如何选择高效的大文件上传控件?

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
银行系统如何选择高效的大文件上传控件?

大文件上传系统开发日记

2023年11月15日 项目启动

客户提出了一个极具挑战性的文件传输系统需求,作为山东的个人开发者,这次接到的项目确实不简单。需求包含20G大文件传输、文件夹结构保持、断点续传、加密传输等多项复杂功能,还要兼容IE8这种"古董"浏览器。今天开始记录开发过程中的关键点和解决方案。

技术选型分析

前端方案

由于需要兼容IE8,我们不得不放弃纯H5方案,转而采用WebUploader作为基础。虽然项目要求原生JS实现,但考虑到开发效率,我们决定以WebUploader为核心进行定制开发。

// 初始化WebUploader实例varuploader=WebUploader.create({auto:false,swf:'Uploader.swf',// Flash文件路径,用于IE兼容server:'/api/upload',pick:'#picker',chunked:true,chunkSize:5*1024*1024,// 分片大小5MBthreads:3,fileNumLimit:1000,fileSizeLimit:20*1024*1024*1024,// 20GBfileSingleSizeLimit:20*1024*1024*1024,duplicate:true,compress:false});

文件夹上传实现

WebUploader本身不支持文件夹上传,我们需要通过递归遍历文件夹结构来实现:

// 文件夹处理函数functionhandleDirectory(files,relativePath=''){for(leti=0;i<files.length;i++){constfile=files[i];if(file.isDirectory){constreader=file.createReader();reader.readEntries(entries=>{handleDirectory(entries,relativePath+file.name+'/');});}else{file.customRelativePath=relativePath;uploader.addFiles(file);}}}// 监听文件夹选择document.getElementById('folderPicker').addEventListener('change',function(e){constentries=e.target.webkitEntries;if(entries&&entries.length>0){handleDirectory(entries);}},false);

2023年11月16日 断点续传实现

断点续传是项目的核心需求之一,需要前后端协同设计。

前端断点续传逻辑

// 文件分片上传前检查是否已上传uploader.on('uploadBeforeSend',function(block,data){data.chunk=block.chunk;data.chunks=block.chunks;data.md5=uploader.md5File(block.file);// 检查分片状态return$.ajax({url:'/api/checkChunk',type:'POST',async:false,data:{md5:data.md5,chunk:data.chunk,chunks:data.chunks,fileName:block.file.name,fileSize:block.file.size,relativePath:block.file.customRelativePath||''},success:function(res){if(res.uploaded){// 该分片已上传,跳过returnfalse;}}});});

后端C#实现 (ASP.NET WebForm)

[WebMethod]publicstaticCheckChunkResultCheckChunk(stringmd5,intchunk,intchunks,stringfileName,longfileSize,stringrelativePath){stringchunkPath=Path.Combine(Server.MapPath("~/App_Data/Upload"),md5);// 检查分片目录是否存在if(!Directory.Exists(chunkPath)){Directory.CreateDirectory(chunkPath);}// 检查分片文件是否存在stringchunkFile=Path.Combine(chunkPath,chunk.ToString());boolexists=File.Exists(chunkFile);returnnewCheckChunkResult{Uploaded=exists,AllUploaded=exists&&Directory.GetFiles(chunkPath).Length==chunks};}

2023年11月17日 加密传输实现

客户要求SM4和AES加密,我们决定在前端实现加密后再传输。

前端加密实现

// 使用CryptoJS实现AES加密functionencryptFileChunk(chunk,key){constwordArray=CryptoJS.lib.WordArray.create(chunk);constencrypted=CryptoJS.AES.encrypt(wordArray,key,{mode:CryptoJS.mode.CFB,padding:CryptoJS.pad.Pkcs7});returnencrypted.toString();}// 文件分片加密处理uploader.on('uploadAccept',function(file,data){if(data.chunk){constkey=sessionStorage.getItem('encryptKey')||'defaultEncryptKey';return{chunkData:encryptFileChunk(data.chunkData,key),isEncrypted:true};}returndata;});

后端解密处理

[WebMethod]publicstaticvoidUploadChunk(stringmd5,intchunk,intchunks,stringfileName,longfileSize,stringrelativePath,stringchunkData,boolisEncrypted){stringchunkPath=Path.Combine(Server.MapPath("~/App_Data/Upload"),md5);stringchunkFile=Path.Combine(chunkPath,chunk.ToString());byte[]data=Convert.FromBase64String(chunkData);if(isEncrypted){// 解密处理data=AESHelper.Decrypt(data,ConfigurationManager.AppSettings["EncryptKey"]);}File.WriteAllBytes(chunkFile,data);// 检查是否所有分片都已上传if(Directory.GetFiles(chunkPath).Length==chunks){MergeFiles(md5,fileName,fileSize,relativePath);}}

2023年11月18日 文件夹结构保持

保持文件夹层级结构是项目的另一个挑战,我们需要在前后端协同处理路径信息。

前端路径处理

// 文件添加到队列时记录相对路径uploader.on('fileQueued',function(file){file.relativePath=file.customRelativePath||'';});

后端C#路径处理

privatestaticvoidMergeFiles(stringmd5,stringfileName,longfileSize,stringrelativePath){stringchunkPath=Path.Combine(Server.MapPath("~/App_Data/Upload"),md5);string[]chunkFiles=Directory.GetFiles(chunkPath).OrderBy(f=>int.Parse(Path.GetFileName(f))).ToArray();// 确保目标目录存在stringdestDirectory=Path.Combine(Server.MapPath("~/Uploads"),Path.GetDirectoryName(relativePath));if(!Directory.Exists(destDirectory)){Directory.CreateDirectory(destDirectory);}stringdestFile=Path.Combine(destDirectory,fileName);using(FileStreamfs=newFileStream(destFile,FileMode.Create,FileAccess.Write)){foreach(stringchunkFileinchunkFiles){byte[]buffer=File.ReadAllBytes(chunkFile);fs.Write(buffer,0,buffer.Length);}}// 上传到阿里云OSSUploadToOSS(destFile,Path.Combine(relativePath,fileName));// 清理临时文件Directory.Delete(chunkPath,true);}

2023年11月19日 IE8兼容方案

为了兼容IE8,我们需要引入Flash后备方案和polyfill。

IE8检测和兼容处理

// 检测IE版本functiongetIEVersion(){varua=window.navigator.userAgent;varmsie=ua.indexOf('MSIE ');if(msie>0){returnparseInt(ua.substring(msie+5,ua.indexOf('.',msie)),10);}returnfalse;}// 根据浏览器选择上传方式if(getIEVersion()&&getIEVersion()<=8){// IE8及以下使用Flash上传uploader.options.server='/api/upload_flash';uploader.options.forceFlash=true;}else{// 现代浏览器使用HTML5上传uploader.options.server='/api/upload_html5';uploader.options.forceFlash=false;}

后端Flash上传处理

[WebMethod]publicstaticvoidUploadFlash(){HttpPostedFilefile=HttpContext.Current.Request.Files[0];stringfileName=HttpContext.Current.Request["name"];stringrelativePath=HttpContext.Current.Request["relativePath"]??"";stringmd5=HttpContext.Current.Request["md5"];// 处理文件保存逻辑stringdestDirectory=Path.Combine(Server.MapPath("~/Uploads"),relativePath);if(!Directory.Exists(destDirectory)){Directory.CreateDirectory(destDirectory);}stringdestFile=Path.Combine(destDirectory,fileName);file.SaveAs(destFile);// 上传到阿里云OSSUploadToOSS(destFile,Path.Combine(relativePath,fileName));}

2023年11月20日 阿里云OSS集成

我们需要将最终文件存储到阿里云OSS,实现加密存储。

C# OSS上传实现

privatestaticvoidUploadToOSS(stringlocalFilePath,stringossPath){stringendpoint=ConfigurationManager.AppSettings["OSSEndpoint"];stringaccessKeyId=ConfigurationManager.AppSettings["OSSAccessKeyId"];stringaccessKeySecret=ConfigurationManager.AppSettings["OSSAccessKeySecret"];stringbucketName=ConfigurationManager.AppSettings["OSSBucketName"];OssClientclient=newOssClient(endpoint,accessKeyId,accessKeySecret);try{// 读取文件内容byte[]fileContent=File.ReadAllBytes(localFilePath);// 加密存储if(ConfigurationManager.AppSettings["EncryptStorage"]=="true"){fileContent=AESHelper.Encrypt(fileContent,ConfigurationManager.AppSettings["StorageEncryptKey"]);}// 上传到OSSMemoryStreamstream=newMemoryStream(fileContent);client.PutObject(bucketName,ossPath,stream);}catch(Exceptionex){// 记录错误日志LogError("OSS上传失败: "+ex.Message);throw;}}

2023年11月21日 文件夹下载功能

客户要求文件夹下载不打包,我们需要实现保持目录结构的下载方式。

前端文件夹下载请求

functiondownloadFolder(folderPath){// 获取文件夹内容列表$.ajax({url:'/api/listFolder',type:'POST',data:{folderPath:folderPath},success:function(files){// 逐个创建下载链接files.forEach(file=>{consta=document.createElement('a');a.href=`/api/download?filePath=${encodeURIComponent(file.path)}`;a.download=file.name;a.style.display='none';document.body.appendChild(a);a.click();document.body.removeChild(a);});}});}

后端文件夹列表和下载

[WebMethod]publicstaticListListFolder(stringfolderPath){stringphysicalPath=Path.Combine(Server.MapPath("~/Uploads"),folderPath);varfiles=newList();if(Directory.Exists(physicalPath)){foreach(stringfileinDirectory.GetFiles(physicalPath,"*",SearchOption.AllDirectories)){files.Add(newFileInfo{path=file.Substring(Server.MapPath("~/Uploads").Length).Replace('\\','/').TrimStart('/'),name=Path.GetFileName(file),size=newFileInfo(file).Length});}}returnfiles;}[WebMethod]publicstaticvoidDownload(stringfilePath){stringphysicalPath=Path.Combine(Server.MapPath("~/Uploads"),filePath);if(File.Exists(physicalPath)){byte[]fileBytes=File.ReadAllBytes(physicalPath);// 解密存储的文件if(ConfigurationManager.AppSettings["EncryptStorage"]=="true"){fileBytes=AESHelper.Decrypt(fileBytes,ConfigurationManager.AppSettings["StorageEncryptKey"]);}HttpContext.Current.Response.ContentType="application/octet-stream";HttpContext.Current.Response.AddHeader("Content-Disposition",$"attachment; filename=\"{Path.GetFileName(filePath)}\"");HttpContext.Current.Response.BinaryWrite(fileBytes);HttpContext.Current.Response.End();}else{HttpContext.Current.Response.StatusCode=404;}}

项目总结

这个项目确实极具挑战性,特别是需要兼容IE8的同时还要实现20G大文件上传和文件夹结构保持。通过本次开发,我总结了以下经验:

  1. 分片上传是大文件传输的关键,需要合理设置分片大小
  2. 断点续传依赖于文件唯一标识(MD5)和分片状态记录
  3. 文件夹结构保持需要在前后端协同处理相对路径
  4. IE8兼容需要使用Flash后备方案
  5. 加密传输应该在前端完成,减少敏感数据暴露

完整的项目代码和文档已经整理完毕,欢迎同行在QQ群(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

创建数据库

配置数据库连接信息

检查数据库配置

访问页面进行测试


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

效果预览

文件上传

文件刷新续传

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

文件夹上传

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

下载完整示例

下载完整示例

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

browseui.dll文件丢失找不到 免费下载方法分享

在使用电脑系统时经常会出现丢失找不到某些文件的情况&#xff0c;由于很多常用软件都是采用 Microsoft Visual Studio 编写的&#xff0c;所以这类软件的运行需要依赖微软Visual C运行库&#xff0c;比如像 QQ、迅雷、Adobe 软件等等&#xff0c;如果没有安装VC运行库或者安装…

作者头像 李华
网站建设 2026/3/31 6:04:36

基于51单片机霍尔测速直流电机控制设计(含源码+原理图+论文+PCB封装)

目录51单片机霍尔测速直流电机控制设计概述核心功能模块硬件设计要点软件设计流程资源文件说明应用与扩展源码文档获取/同行可拿货,招校园代理 &#xff1a;文章底部获取博主联系方式&#xff01;51单片机霍尔测速直流电机控制设计概述 该设计基于51单片机实现直流电机的速度测…

作者头像 李华
网站建设 2026/3/21 10:41:00

智慧水务物联网平台的功能应用

水务行业作为城市基础设施的核心组成部分&#xff0c;面临管网漏损严重、水质监测滞后、水厂运营粗放、防汛响应缓慢等痛点&#xff0c;直接影响居民用水安全与水资源利用效率。对此&#xff0c;数之能基于工业物联网平台&#xff0c;打造 “水源 - 水厂 - 管网 - 用户” 全链路…

作者头像 李华
网站建设 2026/3/13 6:20:20

Python深度学习环境搭建(GPU加速版):从零部署PyTorch全流程实战

第一章&#xff1a;Python深度学习环境搭建概述 构建一个稳定高效的Python深度学习环境是开展模型训练与推理任务的基础。合理的环境配置不仅能避免依赖冲突&#xff0c;还能充分发挥硬件性能&#xff0c;尤其是在使用GPU加速时尤为重要。 选择合适的Python版本与包管理工具 推…

作者头像 李华
网站建设 2026/3/27 13:21:33

揭秘Python打包成exe全过程:5步实现无Python环境运行,新手必看

第一章&#xff1a;揭秘Python打包成exe的核心原理将Python脚本打包为可执行文件&#xff08;.exe&#xff09;的本质&#xff0c;是将Python解释器、依赖库和源代码整合为一个独立运行的程序包。用户无需安装Python环境即可在Windows系统上直接运行&#xff0c;极大提升了部署…

作者头像 李华
网站建设 2026/3/30 20:28:42

收藏!大模型转行/入门全攻略:避坑指南+方向拆解,小白也能看懂

这两年&#xff0c;大模型彻底走出实验室的“象牙塔”&#xff0c;渗透到后端工程师、在校学生、跨行者的日常工作与职业规划中&#xff0c;成为技术圈最热门的赛道之一。 后台每天都被类似的问题刷屏&#xff1a; “我是后端开发&#xff0c;转大模型赛道可行吗&#xff1f;难…

作者头像 李华