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

芯片制造中,C#如何处理文件夹上传的加密需求?

.NET程序员的血泪奋斗史:从0到1搞定大文件上传(含IE8兼容)

咱福建.NET仔最近接了个外包活,客户是做政府资料管理的,需求就一句话:“搞个大文件上传功能,20G文件随便传,文件夹要留层级,IE8也能用,加密传输存储,断点续传不能丢进度!”

客户还补刀:“网上那些开源组件都是垃圾,WebUploader停更了,H5方案IE8跑不动,我要原生JS!预算100块以内,源码要全,文档要细,不然换人!”

得,硬着头皮上吧!以下是我踩坑一周后总结的全栈解决方案,含前后端代码、IE8兼容技巧、加密和断点续传实现,直接复制就能用!


一、需求拆解:客户要的到底是个啥?

用大白话翻译客户需求:

  1. 大文件上传:20G文件分片传,断点续传(关浏览器/重启电脑不丢进度)。
  2. 文件夹保留层级:上传/部门/2024项目/数据.xlsx,下载时也得是这个结构(别给我打包成zip!)。
  3. 加密:传输用SM4/AES,存储也加密(客户说“数据比命重要”)。
  4. 兼容IE8:Win7+IE8的老机器必须能用(甲方爸爸的机器不能碰)。
  5. 后端ASP.NET WebForm:客户不让换技术栈(只能硬啃WebForm)。

二、前端:原生JS + IE8兼容的“土味”方案

IE8不支持File API,不能用H5的分片上传,只能用Flashiframe表单模拟异步。但客户要原生JS,所以选iframe+表单方案(Flash已被淘汰,且客户可能禁用)。

1. 前端核心代码(Vue3兼容IE8?不存在的,IE8只认ES5)
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="UploadPage.aspx.cs" Inherits="BigFileUpload.UploadPage" %> 大文件上传(IE8兼容版) // 全局变量:存储上传任务、进度、加密密钥 var uploadTasks = {}; // 格式:{ fileId: { progress: 0, chunks: [], totalChunks: 0 } } var sm4Key = '客户给的SM4密钥(实际从后端动态获取)'; // 加密密钥(硬编码仅示例,实际要安全存储) // 初始化:绑定文件选择按钮 window.onload = function() { document.getElementById('btnSelect').onclick = function() { document.getElementById('fileInput').click(); // 触发文件选择 }; document.getElementById('fileInput').onchange = handleFileSelect; }; // 处理文件选择(支持文件夹?IE8不支持webkitdirectory,只能手动输入路径) function handleFileSelect(e) { var file = e.target.files[0]; if (!file) return; // 生成唯一fileId(防重复) var fileId = 'file_' + Date.now(); uploadTasks[fileId] = { progress: 0, chunks: [], totalChunks: Math.ceil(file.size / 2 * 1024 * 1024), // 分片大小2MB fileName: file.name, fileSize: file.size }; // 计算文件MD5(用于断点校验,IE8用SparkMD5的兼容版) calculateFileMD5(file, fileId); } // 计算文件MD5(IE8兼容版,用SparkMD5的旧版) function calculateFileMD5(file, fileId) { var chunkSize = 2 * 1024 * 1024; // 2MB/片 var chunks = Math.ceil(file.size / chunkSize); var spark = new SparkMD5.ArrayBuffer(); var reader = new FileReader(); // 分片读取文件(IE8用FileReader的同步模式) function loadNextChunk(currentChunk) { var start = currentChunk * chunkSize; var end = Math.min(start + chunkSize, file.size); reader.readAsArrayBuffer(file.slice(start, end)); // IE8可能不支持slice,改用substring? } reader.onload = function(e) { spark.append(e.target.result); var currentChunk = Math.floor(e.target.result.byteLength / (2 * 1024 * 1024)); if (currentChunk < chunks - 1) { loadNextChunk(currentChunk); } else { var md5 = spark.end(); uploadTasks[fileId].md5 = md5; // 记录MD5(断点校验用) startUpload(fileId); // 开始上传 } }; loadNextChunk(0); } // 开始上传(分片+断点续传) function startUpload(fileId) { var task = uploadTasks[fileId]; var chunkIndex = task.chunks.length; // 下一个要传的分片序号 // 检查是否已上传过该分片(从localStorage读进度) var savedProgress = localStorage.getItem('upload_' + fileId); if (savedProgress) { task.chunks = JSON.parse(savedProgress).chunks; chunkIndex = task.chunks.length; } // 上传当前分片(IE8用iframe模拟异步) var formData = new FormData(); formData.append('fileId', fileId); formData.append('chunkIndex', chunkIndex); formData.append('totalChunks', task.totalChunks); formData.append('fileName', task.fileName); formData.append('chunk', task.file.slice(chunkIndex * 2 * 1024 * 1024, (chunkIndex + 1) * 2 * 1024 * 1024)); var iframe = document.createElement('iframe'); iframe.style.display = 'none'; iframe.src = 'UploadHandler.ashx?action=upload&fileId=' + fileId + '&chunkIndex=' + chunkIndex; document.body.appendChild(iframe); // 监听iframe加载完成(上传结果) iframe.onload = function() { var response = JSON.parse(iframe.contentWindow.document.body.innerText); if (response.code === 200) { task.chunks.push(chunkIndex); task.progress = (task.chunks.length / task.totalChunks) * 100; localStorage.setItem('upload_' + fileId, JSON.stringify(task)); // 保存进度到localStorage if (task.chunks.length === task.totalChunks) { mergeChunks(fileId); // 合并所有分片 } } else { alert('上传失败:' + response.msg); } document.body.removeChild(iframe); }; } // 合并分片(调用后端接口) function mergeChunks(fileId) { var xhr = new XMLHttpRequest(); xhr.open('POST', 'UploadHandler.ashx?action=merge&fileId=' + fileId); xhr.onload = function() { var response = JSON.parse(xhr.responseText); if (response.code === 200) { alert('上传完成!文件路径:' + response.filePath); localStorage.removeItem('upload_' + fileId); // 清除进度 } else { alert('合并失败:' + response.msg); } }; xhr.send(); } 选择文件(支持20G)
2. 前端关键技巧(IE8兼容)
  • 分片上传:用File.slice()(IE8不支持,改用file.substring()?实际测试IE8的File对象不完整,可能需要用ActiveXObject读取文件流,这里简化处理)。
  • 进度保存:用localStorage存上传进度(IE8支持),但容量有限(建议分块存储,比如每5%存一次)。
  • 加密预处理:前端用SM4加密分片(示例未写,需引入sm-crypto库的ES5兼容版)。

三、后端:ASP.NET WebForm(C#)实现分片合并+加密存储

后端需要处理分片接收、断点校验、合并文件、加密存储,还要兼容SQL Server。

1. 后端核心代码(UploadHandler.ashx)
// UploadHandler.ashx(处理上传请求)publicclassUploadHandler:IHttpHandler,IRequiresSessionState{privatestring_uploadDir=HttpContext.Current.Server.MapPath("~/Uploads/Temp/");// 临时分片存储路径privatestring_encryptKey="客户给的SM4密钥(从配置或数据库读取)";// 加密密钥publicvoidProcessRequest(HttpContextcontext){context.Response.ContentType="application/json";stringaction=context.Request["action"];switch(action){case"upload":// 接收分片UploadChunk(context);break;case"merge":// 合并分片MergeChunks(context);break;default:context.Response.Write(JsonConvert.SerializeObject(new{code=400,msg="无效操作"}));break;}}// 接收分片并存储(带SM4加密)privatevoidUploadChunk(HttpContextcontext){stringfileId=context.Request["fileId"];intchunkIndex=int.Parse(context.Request["chunkIndex"]);inttotalChunks=int.Parse(context.Request["totalChunks"]);stringfileName=context.Request["fileName"];// 校验分片是否已上传(防重复)if(IsChunkUploaded(fileId,chunkIndex)){context.Response.Write(JsonConvert.SerializeObject(new{code=200,msg="分片已上传"}));return;}// 读取分片内容(加密传输)HttpPostedFilechunkFile=context.Request.Files["chunk"];byte[]encryptedChunk=newbyte[chunkFile.ContentLength];chunkFile.InputStream.Read(encryptedChunk,0,chunkFile.ContentLength);// SM4解密分片(与前端加密对应)byte[]decryptedChunk=SM4Decrypt(encryptedChunk,_encryptKey);// 保存分片到临时目录stringtempDir=Path.Combine(_uploadDir,fileId);if(!Directory.Exists(tempDir))Directory.CreateDirectory(tempDir);stringchunkPath=Path.Combine(tempDir,$"chunk_{chunkIndex}");File.WriteAllBytes(chunkPath,decryptedChunk);// 记录分片元数据到数据库(SQL Server)SaveChunkMetadata(fileId,chunkIndex,totalChunks,fileName);context.Response.Write(JsonConvert.SerializeObject(new{code=200,msg="分片上传成功"}));}// 合并分片并存储到目标路径privatevoidMergeChunks(HttpContextcontext){stringfileId=context.Request["fileId"];stringtempDir=Path.Combine(_uploadDir,fileId);stringfileName=GetFileNameFromMetadata(fileId);// 从数据库获取文件名stringtargetPath=Path.Combine(HttpContext.Current.Server.MapPath("~/Uploads/"),fileName);// 合并所有分片(流式写入,避免内存溢出)using(FileStreamfs=newFileStream(targetPath,FileMode.Create)){for(inti=0;i<GetTotalChunksFromMetadata(fileId);i++){stringchunkPath=Path.Combine(tempDir,$"chunk_{i}");byte[]decryptedChunk=SM4Decrypt(File.ReadAllBytes(chunkPath),_encryptKey);fs.Write(decryptedChunk,0,decryptedChunk.Length);File.Delete(chunkPath);// 删除临时分片}}// 清理临时目录Directory.Delete(tempDir);// 保存文件元数据到数据库(SQL Server)SaveFileMetadata(fileId,fileName,targetPath);context.Response.Write(JsonConvert.SerializeObject(new{code=200,msg="合并成功",filePath=targetPath}));}// SM4加密工具方法(示例,需引入SM4库)privatebyte[]SM4Encrypt(byte[]data,stringkey){// 实际使用BouncyCastle或专用SM4库实现returndata;// 简化示例}privatebyte[]SM4Decrypt(byte[]data,stringkey){// 实际使用BouncyCastle或专用SM4库实现returndata;// 简化示例}// 数据库操作(示例,使用SqlClient)privatevoidSaveChunkMetadata(stringfileId,intchunkIndex,inttotalChunks,stringfileName){using(SqlConnectionconn=newSqlConnection("Server=.;Database=FileDB;User Id=sa;Password=123456;")){conn.Open();stringsql=@"INSERT INTO FileChunks (FileId, ChunkIndex, TotalChunks, FileName) VALUES (@FileId, @ChunkIndex, @TotalChunks, @FileName)";SqlCommandcmd=newSqlCommand(sql,conn);cmd.Parameters.AddWithValue("@FileId",fileId);cmd.Parameters.AddWithValue("@ChunkIndex",chunkIndex);cmd.Parameters.AddWithValue("@TotalChunks",totalChunks);cmd.Parameters.AddWithValue("@FileName",fileName);cmd.ExecuteNonQuery();}}}
2. 后端关键逻辑
  • 分片校验:通过数据库记录已上传的分片,避免重复上传。
  • 加密存储:分片传输时用SM4加密,后端解密后存储(密钥需安全管理,建议从配置中心获取)。
  • 文件夹结构保留:前端上传时传递文件夹路径(如/部门/2024项目/),后端根据路径创建目录(示例未写,需在前端fileName中包含路径,后端解析后Directory.CreateDirectory)。

四、部署与调试(IE8必看)

  1. 环境准备

    • 服务器装IIS,启用ASP.NET WebForm支持(.NET Framework 4.8)。
    • SQL Server创建数据库FileDB,执行以下脚本建表:
      CREATETABLEFileChunks(IdINTPRIMARYKEYIDENTITY,FileId NVARCHAR(50)NOTNULL,ChunkIndexINTNOTNULL,TotalChunksINTNOTNULL,FileName NVARCHAR(255)NOTNULL,UNIQUE(FileId,ChunkIndex));CREATETABLEFileMetadata(FileId NVARCHAR(50)PRIMARYKEY,FileName NVARCHAR(255)NOTNULL,FilePath NVARCHAR(500)NOTNULL,UploadTimeDATETIMEDEFAULTGETDATE());
  2. IE8兼容调试

    • 用虚拟机装Win7+IE8,测试上传功能(重点测试分片续传、文件夹路径)。
    • 前端fileInputwebkitdirectory属性在IE8无效,需手动输入文件夹路径(如/部门/2024项目/),后端解析后创建目录。

五、源代码与支持

以上代码是简化版,实际需要:

  • 引入SM4加密库(如BouncyCastle的C#版本)。
  • 处理IE8的File对象兼容问题(可能需要用ActiveXObject读取文件流)。
  • 优化分片上传的并发控制(IE8不支持Promise,用setTimeout模拟异步)。

需要完整源码(含加密、文件夹路径处理、SQL Server脚本),可以加群374992201,我免费发你(群里还有红包和接单资源)。

(PS:客户说“能跑起来就行”,但我争取做到“稳如老狗”——毕竟这是吃饭的本事,冲就完事了!)

设置框架

安装.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.jsqmd.com/news/420615/

相关文章:

  • 2026年目前有名的抖音广告代运营企业口碑推荐榜,抖音广告代运营/信息流广告代运营,抖音广告代运营公司推荐排行榜 - 品牌推荐师
  • 青岛口碑不错的隐形车衣专业公司费用怎么算 - mypinpai
  • 露,集成化信息化信号采集处理系统 一体化生物医学信号采集系统 机能集成化信号采集与处理系统
  • LeetCode1680:连接连续二进制数字
  • 以太网接口设备写静态路由的时候为什么写下一跳ip地址?而不是出接口。(ensp模拟说明)
  • 上海宠物口腔健康守护者:2026年优质医生推荐,狗口腔溃疡诊疗/狗狗牙结石/猫咪口腔护理/猫咪口炎,宠物口腔医生哪个靠谱 - 品牌推荐师
  • 哈尔滨沃尔沃XC90俄罗斯STP汽车隔音降噪 有效解决胎噪、风噪、发动机噪音 哈尔滨最专业汽车隔音店-博士达汽车音响倾心打造
  • 科研党收藏!降AI率平台 千笔·降AIGC助手 VS Checkjie,专为本科生定制
  • Snowflake收购数据库迁移技术初创公司Datometry
  • 闭眼入!8个AI论文网站深度测评:继续教育毕业论文写作必备工具推荐
  • 交稿前一晚!降AIGC平台 千笔·专业降AI率智能体 VS WPS AI,继续教育必备神器
  • 别再瞎找了!AI论文平台 千笔ai写作 VS 灵感ai,自考写论文就选它!
  • 6.4 企业数据分析师Agent:数据查询与报告生成实战
  • 6.3 智能客服系统端到端开发:对话管理加RAG加人工转接
  • 什么时候该用接口,什么时候该用抽象类,应该怎么组合才好用,才高级
  • 2026年2月实木家具实力厂家推荐榜,匠心工艺实测解析 - 品牌鉴赏师
  • 大模型不是你家熊孩子:聊聊生成式模型“去偏 / 去毒”流水线怎么落地
  • 想选专业的家装装修设计公司,上海朗域装饰的口碑咋样 - mypinpai
  • 2026年2月木工机械设备供应厂家推荐,资质案例售后深度解读 - 品牌鉴赏师
  • 大二寒假实习小结(嵌入式软件开发岗)
  • 别小看一个“偷看”:Peeking Iterator 背后的设计哲学
  • 001 nvm 管理不同版本的 node 与 npm
  • 枚举是实现接口,匿名内部类扩展function实现
  • 藏家必看!北京上门回收老书3家靠谱机构盘点 避坑科普一次说清 - 品牌排行榜单
  • java8 apply 方法与 Function 接口详解
  • Tcache
  • Qt 开发中复制一个已有的 UI 窗体及其对应的 C++ 类
  • 让机器“长眼睛”:产线视觉检测工程如何重塑质量控制逻辑?
  • 空间智能前向布控系统总体架构设计报告——人员多维感知 × 空间计算 × 融合识别 × 趋势预测 × 联动控制体系
  • 实测对比后,AI论文平台千笔AI VS Checkjie,专科生写作首选!