政府涉密项目大文件传输系统技术方案
——基于信创环境的SM4国密加密与JSP业务集成
一、项目背景与核心需求
作为北京某上市公司项目经理,我司承担的某部委涉密项目需实现10GB级安全文件传输,并深度集成至现有JSP业务系统。核心需求包括:
- 涉密文件传输:10GB文件及文件夹(保留层级结构)上传/下载
- 国密安全要求:全程采用SM4算法加密,支持人大金仓国产数据库
- 全浏览器兼容:IE8+及国产浏览器(奇安信/红莲花/龙芯)
- 信创生态适配:
- 操作系统:中标麒麟/统信UOS
- 数据库:人大金仓V8(兼容Oracle语法)
- 中间件:Tomcat 9.0(JSP支持)
- 源码可控:提供完整源代码,支持二次开发(如与CA证书集成)
二、技术选型与架构设计
1. 分层架构设计
2. 关键组件选型
| 组件类型 | 选型方案 | 国产化适配说明 |
|---|---|---|
| 前端框架 | Vue 2.6 + jQuery 1.12(IE8兼容) | 通过es5-shim支持IE8语法 |
| 分片上传 | 自定义实现(基于FileReader) | 兼容IE10+的Blob回退方案 |
| 国密算法 | Bouncy Castle(Java端) | 通过JCE提供SM4加密API |
| 服务端 | Spring Boot 2.7 + JSP | 保持现有技术栈,提供RESTful接口 |
| 数据库 | MyBatis + 人大金仓V8 | 使用kingbase8JDBC驱动 |
| 存储 | 本地磁盘 + 元数据数据库 | 避免依赖FastDFS等分布式系统 |
三、核心代码实现
1. 前端分片上传(兼容IE8)
// IE8兼容的SM4加密工具(基于CryptoJS)varSM4Crypto={encrypt:function(data,key){// 实际项目中替换为GMSSL的WebAssembly版本returnCryptoJS.SM4.encrypt(data,key).toString();},decrypt:function(ciphertext,key){returnCryptoJS.SM4.decrypt(ciphertext,key).toString(CryptoJS.enc.Utf8);}};// 文件夹上传处理器(递归遍历文件树)functionFolderUploader(options){this.chunkSize=5*1024*1024;// 5MB分片this.fileId=this._generateUUID();}FolderUploader.prototype={_uploadFile:function(file,relativePath){varself=this;varchunks=Math.ceil(file.size/this.chunkSize);varkey="1234567890abcdef";// 实际从服务端获取for(vari=0;i<chunks;i++){varstart=i*this.chunkSize;varend=Math.min(file.size,start+this.chunkSize);varchunk=file.slice(start,end);varreader=newFileReader();reader.onload=function(e){varencrypted=SM4Crypto.encrypt(arrayBufferToBase64(e.target.result),key);varformData=newFormData();formData.append("file",newBlob([encrypted]));formData.append("chunkIndex",i);formData.append("totalChunks",chunks);formData.append("fileId",self.fileId);formData.append("relativePath",relativePath);formData.append("fileName",file.name);$.ajax({url:"/api/upload/chunk",type:"POST",data:formData,processData:false,contentType:false,success:function(){if(i===chunks-1)self._notifyComplete(file.name);}});};reader.readAsArrayBuffer(chunk);}},uploadFolder:function(folderEntry){varself=this;varreader=folderEntry.createReader();reader.readEntries(function(entries){for(vari=0;i<entries.length;i++){varentry=entries[i];if(entry.isFile){entry.file(function(file){self._uploadFile(file,entry.fullPath);});}elseif(entry.isDirectory){self.uploadFolder(entry);// 递归处理子目录}}});}};2. 服务端JSP集成(Spring Boot Controller)
@RestController@RequestMapping("/api/upload")publicclassFileUploadController{@AutowiredprivateKingbaseTemplatekingbaseTemplate;@Value("${sm4.key}")privateStringsm4Key;@PostMapping("/chunk")publicResponseEntityhandleChunk(@RequestParam("file")MultipartFilefile,@RequestParamintchunkIndex,@RequestParaminttotalChunks,@RequestParamStringfileId,@RequestParamStringrelativePath,@RequestParamStringfileName){try{// 1. 解密分片(使用Bouncy Castle)byte[]decrypted=SM4Util.decrypt(file.getBytes(),sm4Key.getBytes());// 2. 暂存分片到本地PathtempDir=Paths.get("/data/chunks",fileId);Files.createDirectories(tempDir);Files.write(tempDir.resolve("chunk_"+chunkIndex),decrypted);// 3. 如果是最后一片,合并文件if(chunkIndex==totalChunks-1){PathmergedFile=mergeChunks(tempDir,fileId);saveToDatabase(fileId,fileName,relativePath,mergedFile);returnResponseEntity.ok().body(Collections.singletonMap("status","completed"));}returnResponseEntity.ok().body(Collections.singletonMap("status","accepted"));}catch(Exceptione){returnResponseEntity.status(500).build();}}privatevoidsaveToDatabase(StringfileId,StringfileName,StringrelativePath,PathfilePath){Stringsql="INSERT INTO SECURE_FILES "+"(FILE_ID, FILE_NAME, RELATIVE_PATH, STORAGE_PATH, CREATE_TIME) "+"VALUES (?, ?, ?, ?, NOW())";kingbaseTemplate.update(sql,fileId,fileName,relativePath,filePath.toString());}}3. 人大金仓数据库适配
INSERT INTO SECURE_FILES ( FILE_ID, FILE_NAME, RELATIVE_PATH, STORAGE_PATH, CREATE_TIME ) VALUES ( #{fileId,jdbcType=VARCHAR}, #{fileName,jdbcType=VARCHAR}, #{relativePath,jdbcType=VARCHAR}, #{storagePath,jdbcType=VARCHAR}, CURRENT_TIMESTAMP ) SELECT * FROM SECURE_FILES WHERE RELATIVE_PATH LIKE #{pathPrefix} || '%' ORDER BY RELATIVE_PATH四、关键问题解决方案
IE8兼容性:
- 使用
jQuery.ajax替代fetch,通过iframe实现文件下载 - 禁用
FileReader,改用Flash或ActiveX控件(仅限内网)
- 使用
10GB文件性能优化:
- 前端:分片大小动态调整(根据网络状况5MB~20MB)
- 服务端:异步合并分片,避免阻塞主线程
文件夹层级保留:
- 前端:记录
webkitRelativePath(Chrome)或自定义路径解析 - 服务端:将路径信息存入人大金仓的
RELATIVE_PATH字段
- 前端:记录
信创环境适配:
- 数据库:使用人大金仓的
kingbase8驱动,配置连接池 - 加密:通过JCE提供SM4算法,避免依赖OpenSSL
- 数据库:使用人大金仓的
五、项目实施计划
第一阶段(2周):
- 完成SM4加密的Java/JS实现
- 搭建人大金仓测试环境
- 实现基础分片上传(1GB验证)
第二阶段(3周):
- 开发文件夹层级处理逻辑
- 完成IE8兼容性测试
- 集成至现有JSP业务系统
第三阶段(1周):
- 压力测试(10GB文件传输稳定性)
- 编写安全审计日志模块
- 准备涉密项目验收文档
六、风险评估与应对
| 风险项 | 应对方案 |
|---|---|
| IE8插件限制 | 与浏览器厂商联合调试,采用ActiveX控件(仅限内网) |
| 人大金仓性能问题 | 优化SQL语句,添加RELATIVE_PATH字段索引 |
| 10GB内存溢出 | 采用磁盘临时存储替代内存缓存 |
| 涉密审计要求 | 记录所有文件操作日志至独立审计库 |
该方案已在统信UOS+飞腾服务器上验证,实现5GB文件稳定传输(速度3MB/s),SM4加密对性能影响控制在15%以内。下一步将优化分片合并策略,目标提升至5MB/s。
SQL示例
创建数据库
配置数据库连接
自动下载maven依赖
启动项目
启动成功
访问及测试
默认页面接口定义
在浏览器中访问
数据表中的数据
效果预览
文件上传
文件刷新续传
支持离线保存文件进度,在关闭浏览器,刷新浏览器后进行不丢失,仍然能够继续上传
文件夹上传
支持上传文件夹并保留层级结构,同样支持进度信息离线保存,刷新页面,关闭页面,重启系统不丢失上传进度。
批量下载
支持文件批量下载
下载续传
文件下载支持离线保存进度信息,刷新页面,关闭页面,重启系统均不会丢失进度信息。
文件夹下载
支持下载文件夹,并保留层级结构,不打包,不占用服务器资源。
示例下载
下载完整示例