news 2026/2/10 11:52:19

金融保险行业如何实现大文件上传下载的安全方案总结?

作者头像

张小明

前端开发工程师

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/2/7 19:46:41

深度测评 9个AI论文工具:本科生毕业论文写作全攻略

随着AI技术的不断进步&#xff0c;越来越多的学术写作工具被推向市场&#xff0c;为本科生撰写毕业论文提供了新的解决方案。然而&#xff0c;面对种类繁多的AI工具&#xff0c;如何选择真正适合自己需求的产品成为一大难题。为此&#xff0c;我们基于2026年的实测数据与用户反…

作者头像 李华
网站建设 2026/2/10 3:53:04

java+vue基于springboot的二手车交易平台系统_594x8878

目录 系统概述技术架构核心功能模块扩展特性部署与优化 开发技术源码文档获取/同行可拿货,招校园代理 &#xff1a;文章底部获取博主联系方式&#xff01; 系统概述 基于SpringBoot和Vue的二手车交易平台系统是一个前后端分离的Web应用&#xff0c;旨在为用户提供车辆买卖、信…

作者头像 李华
网站建设 2026/2/7 14:24:16

AI产品经理十年演进

AI 产品经理&#xff08;AI PM&#xff09; 的十年&#xff08;2015–2025&#xff09;&#xff0c;是从“工程辅助者”向“业务架构师”&#xff0c;再到“智能体导演”的范式跃迁。 这十年中&#xff0c;AI PM 的核心能力从**“懂算法参数”演变为“懂价值对齐”**。如果说早…

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

2026 年企业 HR 选型指南:智能 EHR 系统与传统人事软件核心区别

在企业人力管理数字化进程中&#xff0c;智能 EHR 系统与传统人事软件逐渐成为两种主流选择。前者依托技术革新重构管理逻辑&#xff0c;后者延续传统操作框架满足基础需求&#xff0c;二者的差异直接影响 HR 工作效率、企业人才管理质量。很多 HR 在选型时困惑于如何区分二者核…

作者头像 李华
网站建设 2026/2/8 1:42:53

FaceSeeker 慧眼寻人 v1.6 本地 AI 批量人脸识别工具

FaceSeeker 慧眼寻人 v1.6 是一款本地 AI 驱动的批量人脸识别工具&#xff0c;核心聚焦照片批量查找需求&#xff0c;凭借高准确率识别与多核高速处理能力&#xff0c;适配日常图片整理、特定人物筛选等场景&#xff0c;为用户提供安全高效的人脸检索解决方案&#xff0c;无需依…

作者头像 李华
网站建设 2026/2/8 14:09:02

Canvas表格绘制教程:网格线、文本填充与优化技巧

在数据可视化需求日益增长的今天&#xff0c;HTML5 Canvas 提供了强大的图形绘制能力。对于需要高度自定义的表格而言&#xff0c;直接使用 Canvas 绘制成为一种灵活但具有挑战性的选择。它不像操作 DOM 那样便捷&#xff0c;需要开发者亲手控制每一个像素的布局与样式&#xf…

作者头像 李华