news 2026/2/16 10:16:51

内网环境下,html5如何支持大文件的分块上传?

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
内网环境下,html5如何支持大文件的分块上传?

中石油旗下子公司大文件传输系统技术方案

一、项目背景与需求分析

作为中石油集团旗下专注于能源信息化领域的子公司,我司长期服务于政府及军工单位,在能源管理、安全生产等关键领域积累了丰富的行业经验。本次政府招投标项目提出的大文件传输需求具有以下特殊性:

  1. 功能需求

    • 支持20GB以上超大文件传输
    • 支持文件夹结构上传/下载(保持目录层级)
    • 支持断点续传、分片校验、传输加密
    • 支持传输队列管理(优先级控制)
  2. 兼容性要求

    • 主流浏览器(Chrome/Firefox/Edge/国产浏览器)
    • 信创国产化环境(麒麟/统信UOS + 飞腾/鲲鹏/龙芯)
    • 数据库兼容性(SQL Server/达梦/人大金仓)
  3. 技术架构约束

    • 后端:ASP.NET Framework 4.8(暂不考虑.NET Core迁移)
    • 前端:ASP.NET WebForms(需兼容IE11及现代浏览器)
    • 需提供完整源代码及知识产权
  4. 特殊需求

    • 军工级安全要求(等保三级)
    • 传输过程不可逆加密
    • 详细的操作审计日志

二、现有方案评估

2.1 已评估开源方案

组件名称优点缺点
WebUploader成熟度高已停更(最后更新2018年),不支持信创环境
Fine Uploader商业级功能许可证成本高,定制开发受限
Dropzone.js简单易用仅支持基础上传,缺乏大文件处理能力
Plupload多后端支持文档陈旧,.NET实现效率低

2.2 核心问题

  1. 信创环境兼容性:现有组件均未针对国产CPU架构和操作系统进行优化
  2. 技术架构限制:ASP.NET WebForms的异步处理能力有限
  3. 安全合规性:开源组件缺乏军工级加密和审计功能
  4. 文件夹支持:大多数组件仅支持单文件上传

三、自研组件技术方案

3.1 架构设计

┌─────────────────────┐ ┌─────────────────────┐ ┌─────────────────────┐ │ ASP.NET WebForms │ │ IIS (WebDAV) │ │ WCF/ASP.NET服务 │ │ (前端上传控件) │←──→│ (静态资源+代理) │←──→│ (传输处理服务) │ └─────────────────────┘ └─────────────────────┘ └─────────────────────┘ ↑ ↑ ↑ HTML5 File API SignalR实时通知 ADO.NET多数据库适配 (Folder API polyfill) (传输进度推送) (SQL Server/达梦/人大金仓)

3.2 核心代码实现

前端实现(ASP.NET WebForms控件)
// FileTransferControl.ascx.cspublicpartialclassFileTransferControl:System.Web.UI.UserControl{protectedvoidPage_Load(objectsender,EventArgse){if(!IsPostBack){RegisterClientScripts();}}privatevoidRegisterClientScripts(){stringscript=@" // 文件夹选择兼容性处理(IE11+现代浏览器) function handleFolderSelect(event) { const input = event.target; if (input.files && input.files.length > 0) { processFiles(input.files); } } // 分片上传核心逻辑 async function uploadFile(file, options) { const chunkSize = 10 * 1024 * 1024; // 10MB分片 const chunks = Math.ceil(file.size / chunkSize); let uploadedChunks = 0; // 计算文件SM3哈希(国产密码算法) const fileHash = await calculateSM3Hash(file); // 检查文件是否已存在 const checkResult = await $.ajax({ url: 'FileTransferHandler.ashx?action=check', type: 'POST', data: { fileName: file.name, fileSize: file.size, fileHash } }); if (checkResult.exists) { showProgress(file.name, 100); return; } // 分片上传 for (let i = 0; i < chunks; i++) { const start = i * chunkSize; const end = Math.min(start + chunkSize, file.size); const chunk = file.slice(start, end); const formData = new FormData(); formData.append('file', chunk); formData.append('fileId', options.fileId); formData.append('chunkIndex', i); formData.append('totalChunks', chunks); formData.append('fileHash', fileHash); formData.append('relativePath', options.relativePath || ''); await $.ajax({ url: 'FileTransferHandler.ashx?action=upload', type: 'POST', data: formData, processData: false, contentType: false, xhr: function() { const xhr = new XMLHttpRequest(); xhr.upload.addEventListener('progress', function(e) { if (e.lengthComputable) { const progress = Math.round( (uploadedChunks * chunkSize + e.loaded) / file.size * 100 ); showProgress(file.name, progress); } }, false); return xhr; } }); uploadedChunks++; } // 通知服务器合并 await $.ajax({ url: 'FileTransferHandler.ashx?action=merge', type: 'POST', data: { fileId: options.fileId, fileHash, totalChunks: chunks, originalName: file.name, relativePath: options.relativePath } }); showProgress(file.name, 100); } // SM3哈希计算(通过Web Worker) function calculateSM3Hash(file) { return new Promise((resolve) => { const worker = new Worker('/Scripts/sm3.worker.js'); worker.postMessage({ file }); worker.onmessage = (e) => resolve(e.data.hash); }); }";ScriptManager.RegisterStartupScript(this,GetType(),"FileTransferScript",script,true);}protectedvoidbtnUpload_Click(objectsender,EventArgse){// 服务器端初始化上传会话stringsessionId=Guid.NewGuid().ToString();Session["FileTransfer_"+sessionId]=newFileTransferSession{StartTime=DateTime.Now,UserId=User.Identity.Name,ClientIP=Request.UserHostAddress};hdnSessionId.Value=sessionId;}}
后端实现(ASP.NET通用处理程序)
// FileTransferHandler.ashx.cspublicclassFileTransferHandler:IHttpHandler{privatereadonlyIFileStorageService_storageService;privatereadonlyIAuditLogService_auditLog;publicFileTransferHandler(){// 通过依赖注入或服务定位器获取服务_storageService=ServiceLocator.Current.GetInstance();_auditLog=ServiceLocator.Current.GetInstance();}publicvoidProcessRequest(HttpContextcontext){context.Response.ContentType="application/json";try{varaction=context.Request["action"];varsessionId=context.Request["sessionId"]??context.Request.Cookies["FileTransferSession"];varsession=context.Session["FileTransfer_"+sessionId]asFileTransferSession;if(session==null){thrownewInvalidOperationException("无效的传输会话");}switch(action?.ToLower()){case"check":HandleCheckRequest(context,session);break;case"upload":HandleUploadRequest(context,session);break;case"merge":HandleMergeRequest(context,session);break;case"download":HandleDownloadRequest(context,session);break;default:thrownewNotSupportedException("不支持的操作");}}catch(Exceptionex){context.Response.StatusCode=500;context.Response.Write(JsonConvert.SerializeObject(new{success=false,message=ex.Message}));// 记录错误日志_auditLog.LogError("FileTransfer",ex.ToString(),context.Request.UserHostAddress,User.Identity.Name);}}privatevoidHandleCheckRequest(HttpContextcontext,FileTransferSessionsession){varfileName=context.Request["fileName"];varfileSize=long.Parse(context.Request["fileSize"]);varfileHash=context.Request["fileHash"];// 多数据库适配查询varexists=_storageService.CheckFileExists(fileHash,fileSize);context.Response.Write(JsonConvert.SerializeObject(new{success=true,exists,fileId=Guid.NewGuid().ToString()}));// 记录审计日志_auditLog.LogAccess("FileCheck",$"{fileName}({fileSize}bytes)",context.Request.UserHostAddress,User.Identity.Name);}privatevoidHandleUploadRequest(HttpContextcontext,FileTransferSessionsession){if(context.Request.Files.Count==0){thrownewArgumentException("未接收到文件数据");}varfile=context.Request.Files[0];varfileId=context.Request["fileId"];varchunkIndex=int.Parse(context.Request["chunkIndex"]);vartotalChunks=int.Parse(context.Request["totalChunks"]);varfileHash=context.Request["fileHash"];varrelativePath=context.Request["relativePath"];// 临时存储路径(按会话ID分目录)vartempPath=Path.Combine(_storageService.TempDirectory,fileId);Directory.CreateDirectory(tempPath);varchunkPath=Path.Combine(tempPath,$"chunk-{chunkIndex}");file.SaveAs(chunkPath);// 记录分片上传日志(数据库适配)using(vardb=DatabaseFactory.CreateDatabase()){db.ExecuteNonQuery("INSERT INTO FileTransferChunks (FileId, ChunkIndex, Size, UploadTime) "+"VALUES (@fileId, @chunkIndex, @size, GETDATE())",fileId,chunkIndex,file.ContentLength);}context.Response.Write(JsonConvert.SerializeObject(new{success=true,chunkIndex,receivedSize=file.ContentLength}));}// 其他处理方法...}
多数据库适配层
// DatabaseFactory.cspublicstaticclassDatabaseFactory{privatestaticreadonlystring_dbType=ConfigurationManager.AppSettings["DatabaseType"];publicstaticIDatabaseCreateDatabase(){switch(_dbType?.ToUpper()){case"DM":returnnewDamengDatabaseAdapter();case"KINGBASE":returnnewKingbaseDatabaseAdapter();case"SQLSERVER":default:returnnewSqlServerDatabaseAdapter();}}}publicinterfaceIDatabase:IDisposable{intExecuteNonQuery(stringsql,paramsobject[]parameters);objectExecuteScalar(stringsql,paramsobject[]parameters);DataTableExecuteDataTable(stringsql,paramsobject[]parameters);}// SQL Server实现publicclassSqlServerDatabaseAdapter:IDatabase{privateSqlConnection_connection;publicSqlServerDatabaseAdapter(){_connection=newSqlConnection(ConfigurationManager.ConnectionStrings["SqlServer"].ConnectionString);_connection.Open();}publicintExecuteNonQuery(stringsql,paramsobject[]parameters){using(varcmd=newSqlCommand(sql,_connection)){cmd.Parameters.AddRange(parameters.Select((p,i)=>newSqlParameter($"@param{i}",p??DBNull.Value)).ToArray());returncmd.ExecuteNonQuery();}}// 其他方法实现...publicvoidDispose(){_connection?.Dispose();}}// 达梦数据库实现publicclassDamengDatabaseAdapter:IDatabase{privateDmConnection_connection;publicDamengDatabaseAdapter(){_connection=newDmConnection(ConfigurationManager.ConnectionStrings["Dameng"].ConnectionString);_connection.Open();}publicintExecuteNonQuery(stringsql,paramsobject[]parameters){// 达梦特定语法适配sql=sql.Replace("GETDATE()","SYSDATE").Replace("[","\"").Replace("]","\"");using(varcmd=newDmCommand(sql,_connection)){cmd.Parameters.AddRange(parameters.Select((p,i)=>newDmParameter($":param{i}",p??DBNull.Value)).ToArray());returncmd.ExecuteNonQuery();}}// 其他方法实现...}

3.3 信创环境适配方案

  1. 国产密码算法集成

    // SM3哈希计算(通过P/Invoke调用国产密码库)publicstaticclassSM3Helper{[DllImport("GMSSL.dll",EntryPoint="SM3_Compute",CharSet=CharSet.Ansi)]privatestaticexternintNativeSM3Compute(byte[]input,intinputLen,byte[]output);publicstaticstringComputeHash(Streamstream){constintbufferSize=8192;byte[]buffer=newbyte[bufferSize];intbytesRead;using(varsm3=SM3.Create())// 使用BouncyCastle的SM3实现或国产密码库{while((bytesRead=stream.Read(buffer,0,bufferSize))>0){sm3.TransformBlock(buffer,0,bytesRead,null,0);}sm3.TransformFinalBlock(buffer,0,0);returnBitConverter.ToString(sm3.Hash).Replace("-","").ToLower();}}}
  2. 国产操作系统文件系统优化

    publicclassKylinFileOptimizer:IFileOptimizer{publicvoidOptimizeFileHandle(FileInfofile){if(IsKylinOS()){// 调用国产文件系统特性NativeMethods.SetFileAttribute(file.FullName,FileAttributes.KylinOptimized);}}privateboolIsKylinOS(){returnDirectory.Exists("/etc/kylin")||Directory.Exists("/usr/lib/kylin");}}
  3. Web服务器配置

四、实施路线图

  1. 第一阶段(6周)

    • 完成核心传输功能开发
    • 实现文件夹结构解析与重建
    • 完成SQL Server基础版本
  2. 第二阶段(4周)

    • 达梦/人大金仓数据库适配
    • 信创环境专项优化
    • 军工级安全加固
  3. 第三阶段(2周)

    • 性能调优与压力测试
    • 操作手册与API文档编写
    • 知识产权梳理与源代码审计

五、预期收益

  1. 完全自主可控:掌握全部源代码,适应军工项目特殊要求
  2. 多数据库支持:无缝切换SQL Server/达梦/人大金仓
  3. 信创环境优化:针对国产软硬件环境深度优化
  4. 安全合规:满足等保三级要求,集成国产密码算法
  5. 性能提升:通过分片并行传输,充分利用网络带宽

我司已组建由.NET资深专家领衔的专项团队,整合集团内部密码技术资源,预计在3个月内完成符合军工标准的大文件传输系统开发。该系统不仅满足当前项目需求,更可打造为能源行业信创解决方案的核心组件。

将组件复制到项目中

示例中已经包含此目录

引入组件

配置接口地址

接口地址分别对应:文件初始化,文件数据上传,文件进度,文件上传完毕,文件删除,文件夹初始化,文件夹删除,文件列表
参考:http://www.ncmem.com/doc/view.aspx?id=e1f49f3e1d4742e19135e00bd41fa3de

处理事件

启动测试

启动成功

效果

数据库

下载示例

点击下载完整示例

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

用过才敢说 10个AI论文网站测评!本科生毕业论文写作必备工具推荐

在当前学术写作日益依赖AI工具的背景下&#xff0c;本科生在毕业论文写作过程中常面临选题困难、文献检索繁琐、格式规范不熟、改稿效率低等挑战。为了帮助学生更高效地完成论文&#xff0c;笔者基于2026年的实测数据与用户反馈&#xff0c;对市面上主流的AI论文工具进行了系统…

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

发票红章盖住金额怎么办

财务人员在日常工作中&#xff0c;常常遇到一个让人头疼的问题&#xff1a;发票上的红色印章不小心盖到了金额区域&#xff0c;导致关键数字被遮挡。这种情况到底能不能用&#xff1f;又该如何处理&#xff1f;本文将从实务操作和技术解决方案两个角度&#xff0c;帮你彻底搞定…

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

ISO 13485深度解析:医疗设备软件开发的实践指南

1 ISO 13485&#xff1a;医疗设备质量的“操作系统”从医疗设备软件开发的角度看&#xff0c;ISO 13485可以被理解为整个医疗器械生命周期的“操作系统”。它不是一个具体的技术标准&#xff0c;而是一个框架性的质量管理体系&#xff08;QMS&#xff09;标准&#xff0c;专门为…

作者头像 李华
网站建设 2026/2/14 23:28:10

Agentic Coding一些实践总结

最近一年AI编码飞速发展&#xff0c;现在我的90%以上的代码都是出自AI。想想在2024年调用OpneAI官方接口&#xff08;3.5模型&#xff09;,超过10K的Token就让LLM的上下文完全混乱&#xff0c;导致LLM无法记住太多东西&#xff0c;更不用说调用工具&#xff0c;生成代码了。到目…

作者头像 李华
网站建设 2026/2/16 0:31:03

String池原理详解:Java字符串创建与内存优化

理解String池是掌握Java内存管理和性能优化的关键一步。它本质上是一个位于堆内存中的字符串常量池&#xff0c;用于存储字符串字面量&#xff0c;其核心目的是通过重用不可变的字符串对象来节省内存、提升程序效率。对于开发者而言&#xff0c;深入理解其工作机制能有效避免一…

作者头像 李华
网站建设 2026/2/15 3:09:28

VS2026对比VSS2005:版本控制工具新老差异与迁移指南

从VSS2005到VS2026&#xff0c;版本控制工具经历了革命性的变化。作为一名长期使用微软技术栈的开发者&#xff0c;我见证了从集中式、文件锁定的旧模式&#xff0c;到如今分布式、高效协作的现代开发流程。这种演进不仅仅是工具的升级&#xff0c;更是开发理念的深刻变革。 V…

作者头像 李华