曲靖市网站建设_网站建设公司_Java_seo优化
2026/1/3 19:29:44 网站建设 项目流程

广西金融行业银行单位大文件传输解决方案

作为广西金融行业银行单位上市公司项目负责人,我负责的集团金融系统需实现——高安全、强兼容、稳落地、可扩展。结合集团现有系统架构(SpringBoot后端+多技术栈前端)与客户严格需求(100G文件传输、信创国产化、断点续传、SM4/AES加密),我主导设计了一套全栈信创适配、源码级可控的大文件传输解决方案,以下从技术实现、源码架构、合规保障三方面展开说明:


一、方案设计核心要点

1. 功能全景覆盖

需求维度技术实现要点
100G文件传输分片上传(5MB/片)+ 断点续传(localStorage+数据库双持久化)+ 流式下载(分块读取)
文件夹层级保留现代浏览器webkitdirectory自动采集相对路径;IE8/9手动输入路径+后端路径映射表
断点续传稳定性进度信息同时存储于localStorage(前端)与upload_progress表(后端),双重校验
加密体系传输层HTTPS+存储层SM4(国密主用)/AES(可选)+ 密钥管理系统(KMS)集中管控
信创国产化适配支持统信UOS/麒麟OS、达梦/人大金仓数据库、华为OBS(公有云/私有云)
多技术栈兼容前端封装为独立组件(支持Vue2/Vue3/JSP/.NET),后端提供RESTful API(无框架强绑定)

2. 兼容性保障策略

  • 浏览器兼容
    • IE8/9:降级使用XMLHttpRequest+FormData(原生支持),手动输入文件夹路径(通过prompt采集),localStorage存储进度(IE8需引入es5-shim)。
    • 信创浏览器(龙芯/红莲花/奇安信):基于W3C标准实现,禁用浏览器私有特性,通过feature detection动态适配。
    • 现代浏览器(Chrome/Firefox):利用File APIBlobslice等特性优化分片效率。
  • 操作系统适配
    • 前端代码通过Babel转译ES6+语法,兼容Windows 7+IE8至统信UOS最新版。
    • 后端Java代码无操作系统依赖,通过System.getProperty("os.name")动态适配文件路径分隔符(/\)。
  • 数据库适配
    • 使用SpringAbstractRoutingDataSource实现多数据源路由,支持MySQL/Oracle/达梦/人大金仓动态切换(配置文件指定db.type)。

3. 安全合规设计

  • 数据传输:强制HTTPS(TLS 1.2+),前端请求头添加X-Signature(SM3哈希+私钥签名防篡改)。
  • 数据存储:文件加密后存储至华为OBS(私有桶),密钥由KMS管理(SM4密钥长度128bit,AES-256),密钥与文件哈希绑定存储。
  • 权限控制:集成集团LDAP/AD系统,通过@PreAuthorize注解实现文件操作权限校验(如“仅上传者可下载”)。

二、前端核心源码实现(Vue2兼容版)

1. 文件夹上传组件(支持层级结构+IE8)

// 兼容IE8的工具函数 if (!Array.prototype.forEach) { Array.prototype.forEach = function(fn) { for (var i=0; i<this.length; i++) fn(this[i], i); }; } if (!Object.keys) { Object.keys = function(obj) { return Object.getOwnPropertyNames(obj); }; } export default { data() { return { fileList: [], // 文件列表(含id、path、size、status、progress、chunks、uploadedChunks) chunkSize: 1024 * 1024 * 5, // 5MB/片(IE8内存限制) uploadUrl: '/api/upload/chunk', // 后端分片接口 statusText: { pending: '等待', uploading: '上传中', success: '成功', failed: '失败' }, isIeLegacy: /*@cc_on!@*/false // IE8检测 }; }, methods: { // 触发文件选择(兼容IE8) handleBrowse() { const input = document.createElement('input'); input.type = 'file'; input.style.display = 'none'; if (this.isIeLegacy) { // IE8不支持webkitdirectory,手动输入路径 this.$alert('请手动输入文件夹路径(如:D:/业务文件)', '提示', { confirmButtonText: '确定', callback: (action) => { if (action === 'confirm') { const path = prompt('请输入文件夹路径'); if (path) this.mockFolderFiles(path); // 模拟读取文件夹(实际需后端遍历) } } }); } else { input.setAttribute('webkitdirectory', ''); input.addEventListener('change', this.handleFileChange); } document.body.appendChild(input); input.click(); document.body.removeChild(input); }, // 现代浏览器文件夹处理 handleFileChange(e) { const files = e.target.files; if (!files.length) return; this.processFiles(files); }, // 递归遍历文件夹(现代浏览器) async processFiles(files) { const fileEntries = []; for (let i = 0; i < files.length; i++) { const file = files[i]; const entry = file.webkitGetAsEntry(); if (entry.isDirectory) { await this.traverseDirectory(entry, ''); } else { fileEntries.push({ name: file.webkitRelativePath, size: file.size, file: file }); } } this.generateHashes(fileEntries); // 生成文件哈希(断点续传校验) }, // 递归遍历目录(现代浏览器) traverseDirectory(entry, parentPath) { return new Promise((resolve) => { entry.file((file) => { const relativePath = parentPath + file.name; if (entry.isDirectory) { const dirReader = entry.createReader(); dirReader.readEntries((entries) => { entries.forEach((e) => this.traverseDirectory(e, `${relativePath}/`).then(resolve)); }); } else { this.fileList.push({ id: Date.now() + Math.random().toString(36).substr(2), path: relativePath, size: file.size, file: file, status: 'pending', progress: 0, chunks: Math.ceil(file.size / this.chunkSize), uploadedChunks: 0 }); } }); resolve(); }); }, // 生成文件哈希(SparkMD5,兼容IE8需引入兼容版本) generateHashes(files) { files.forEach((fileObj) => { const reader = new FileReader(); reader.onload = (e) => { const spark = new SparkMD5.ArrayBuffer(); spark.append(e.target.result); const hash = spark.end(); fileObj.hash = hash; this.checkUploadProgress(fileObj); // 检查后端进度 }; reader.readAsArrayBuffer(fileObj.file); }); }, // 检查断点续传进度(localStorage+后端双校验) async checkUploadProgress(fileObj) { // 从localStorage获取本地进度 const localProgress = localStorage.getItem(`upload_${fileObj.hash}`) || '{}'; const { uploadedChunks } = JSON.parse(localProgress); fileObj.uploadedChunks = uploadedChunks; // 从后端获取服务端进度(关键:刷新/关闭浏览器后恢复) try { const res = await this.$http.get(`/api/upload/check?hash=${fileObj.hash}`); fileObj.uploadedChunks = res.data.uploadedChunks || 0; localStorage.setItem(`upload_${fileObj.hash}`, JSON.stringify({ uploadedChunks: res.data.uploadedChunks })); } catch (err) { console.error('检查进度失败:', err); } if (fileObj.uploadedChunks >= fileObj.chunks) { fileObj.status = 'success'; fileObj.progress = 100; } else { this.startChunkUpload(fileObj); } }, // 分片上传(兼容IE8) startChunkUpload(fileObj) { fileObj.status = 'uploading'; for (let i = fileObj.uploadedChunks; i < fileObj.chunks; i++) { const start = i * this.chunkSize; const end = Math.min(start + this.chunkSize, fileObj.size); const chunk = fileObj.file.slice(start, end); // 前端加密(SM4示例,需后端配合密钥) const encryptedChunk = this.encryptChunk(chunk, fileObj.hash); // 构造FormData(IE8原生支持) const formData = new FormData(); formData.append('file', encryptedChunk); formData.append('hash', fileObj.hash); formData.append('chunk', i); formData.append('total', fileObj.chunks); formData.append('path', fileObj.path); // 上传请求(XMLHttpRequest兼容IE8) const xhr = new XMLHttpRequest(); xhr.open('POST', this.uploadUrl, true); xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); // 标识AJAX请求 xhr.upload.onprogress = (e) => { if (e.lengthComputable) { const progress = Math.round(((i + 1) / fileObj.chunks) * 100); fileObj.progress = progress; localStorage.setItem(`upload_${fileObj.hash}`, JSON.stringify({ uploadedChunks: i + 1 })); } }; xhr.onload = () => { if (xhr.status === 200) { fileObj.uploadedChunks++; if (fileObj.uploadedChunks === fileObj.chunks) { fileObj.status = 'success'; fileObj.progress = 100; localStorage.removeItem(`upload_${fileObj.hash}`); // 清除进度 this.mergeChunks(fileObj); // 触发合并 } } else { fileObj.status = 'failed'; this.$message.error(`分片${i}上传失败`); } }; xhr.onerror = () => { fileObj.status = 'failed'; this.$message.error('网络异常,上传中断'); }; xhr.send(formData); } }, // 合并分片(调用后端接口) mergeChunks(fileObj) { this.$http.post('/api/upload/merge', { hash: fileObj.hash, path: fileObj.path, totalChunks: fileObj.chunks }).then(() => { this.$message.success('文件上传完成'); }).catch((err) => { this.$message.error(`合并失败:${err.message}`); }); }, // SM4加密分片(需引入sm-crypto库) encryptChunk(chunk, fileHash) { const key = localStorage.getItem(`sm4_key_${fileHash}`) || 'default_sm4_key_128bit'; // 实际应从KMS获取 return smCrypto.sm4.encrypt(chunk, key); }, // 辅助函数:格式化文件大小 formatSize(size) { if (size >= 1024 ** 3) return `${(size / 1024 ** 3).toFixed(2)}GB`; if (size >= 1024 ** 2) return `${(size / 1024 ** 2).toFixed(2)}MB`; return `${(size / 1024).toFixed(2)}KB`; } } }; .file-upload { max-width: 1200px; margin: 20px auto; padding: 20px; border: 1px solid #ebeef5; border-radius: 4px; } .progress-table { width: 100%; border-collapse: collapse; margin-top: 15px; } .progress-table th, .progress-table td { padding: 12px; text-align: left; border-bottom: 1px solid #ebeef5; } .progress-bar { width: 200px; height: 20px; background: #f5f7fa; border-radius: 10px; overflow: hidden; } .progress { height: 100%; background: #409eff; transition: width 0.3s; } .status-uploading { color: #e6a23c; } .status-success { color: #67c23a; } .status-failed { color: #f56c6c; }

2. 非打包下载组件(支持100G级+50MB/S)

export default { data() { return { downloadList: [] // 文件列表(含id、path、size、url、hash) }; }, methods: { // 获取文件夹文件列表(调用后端接口) fetchFolderFiles(folderId) { this.$http.get(`/api/file/list?folderId=${folderId}`).then((res) => { this.downloadList = res.data.map((file) => ({ id: file.id, path: file.originalPath, size: file.size, url: `/api/file/download?hash=${file.hash}&path=${encodeURIComponent(file.originalPath)}`, hash: file.hash })); }); }, // 单个文件下载(流式输出,避免内存溢出) handleSingleDownload(file) { const a = document.createElement('a'); a.href = file.url; a.download = file.path.split('/').pop(); // 仅下载文件名(保留路径需后端配合) document.body.appendChild(a); a.click(); document.body.removeChild(a); }, // 批量下载(间隔500ms防拦截) handleBatchDownload() { if (this.downloadList.length === 0) { this.$message.warning('请先选择文件夹'); return; } this.downloadList.forEach((file, index) => { setTimeout(() => { this.handleSingleDownload(file); }, index * 500); }); }, // 辅助函数:格式化文件大小 formatSize(size) { if (size >= 1024 ** 3) return `${(size / 1024 ** 3).toFixed(2)}GB`; if (size >= 1024 ** 2) return `${(size / 1024 ** 2).toFixed(2)}MB`; return `${(size / 1024).toFixed(2)}KB`; } } };

三、后端核心源码实现(SpringBoot)

1. 分片上传与断点续传接口(支持多数据库)

@RestController@RequestMapping("/api/upload")publicclassUploadController{@AutowiredprivateUploadServiceuploadService;@AutowiredprivateStorageServicestorageService;// 云存储抽象层(OBS/本地)@AutowiredprivateEncryptionServiceencryptionService;// 加密服务(SM4/AES)// 分片上传(支持断点续传)@PostMapping("/chunk")publicResponseEntityuploadChunk(@RequestParam("file")MultipartFilechunk,@RequestParam("hash")StringfileHash,@RequestParam("chunk")IntegerchunkIndex,@RequestParam("total")IntegertotalChunks,@RequestParam("path")StringoriginalPath){// 1. 解密分片(若启用加密)if(encryptionService.isEncrypted()){chunk=encryptionService.decryptChunk(chunk,fileHash);}// 2. 保存分片到临时目录(格式:{tempDir}/{fileHash}/{chunkIndex})StringtempDir=storageService.getTempDir(fileHash);FilechunkFile=newFile(tempDir,String.valueOf(chunkIndex));chunk.transferTo(chunkFile);// 3. 更新数据库分片状态(支持MySQL/达梦/人大金仓)uploadService.updateChunkStatus(fileHash,chunkIndex,totalChunks);returnResponseEntity.ok().build();}// 合并分片(触发文件合并与加密存储)@PostMapping("/merge")publicResponseEntitymergeChunks(@RequestBodyMergeRequestrequest){StringfileHash=request.getHash();IntegertotalChunks=request.getTotalChunks();StringoriginalPath=request.getPath();// 1. 校验所有分片是否上传完成if(!uploadService.isAllChunksUploaded(fileHash,totalChunks)){returnResponseEntity.badRequest().body("分片未全部上传");}// 2. 合并分片到临时文件FilemergedFile=storageService.mergeTempChunks(fileHash,totalChunks);// 3. 加密存储(SM4/AES)StringencryptedPath=storageService.encryptAndStore(mergedFile,fileHash);// 4. 清理临时文件storageService.cleanTempFiles(fileHash);// 5. 保存文件元数据(支持多数据库)uploadService.saveFileInfo(fileHash,originalPath,encryptedPath);returnResponseEntity.ok().build();}// 检查断点续传进度(关键:刷新/关闭浏览器后恢复)@GetMapping("/check")publicResponseEntitycheckProgress(@RequestParam("hash")StringfileHash){IntegeruploadedChunks=uploadService.getUploadedChunks(fileHash);IntegertotalChunks=uploadService.getTotalChunks(fileHash);Integerprogress=totalChunks==0?0:(uploadedChunks*100)/totalChunks;returnResponseEntity.ok(newProgressVO(progress,uploadedChunks,totalChunks));}}// 辅助类:合并请求@DataclassMergeRequest{privateStringhash;privateStringpath;privateIntegertotalChunks;}// 辅助类:进度视图对象@Data@AllArgsConstructorclassProgressVO{privateIntegerprogress;privateIntegeruploadedChunks;privateIntegertotalChunks;}

2. 存储抽象层(支持华为OBS+多数据库)

@ServicepublicclassStorageService{@Value("${storage.type:obs}")// 动态配置:obs/localprivateStringstorageType;@AutowiredprivateObsClientobsClient;// 华为OBS客户端(私有云需配置AK/SK)@Value("${local.storage.path:/data/upload/temp}")privateStringlocalStoragePath;// 获取临时目录(支持多数据库路径兼容)publicStringgetTempDir(StringfileHash){Stringos=System.getProperty("os.name").toLowerCase();Stringseparator=os.contains("win")?"\\":"/";returnString.format("%s%s%s%s",localStoragePath,separator,"temp",separator,fileHash);}// 合并临时分片(流式操作,避免内存溢出)publicFilemergeTempChunks(StringfileHash,IntegertotalChunks)throwsIOException{FiletempDir=newFile(getTempDir(fileHash));FilemergedFile=newFile(tempDir.getParent(),fileHash+".dat");try(RandomAccessFileraf=newRandomAccessFile(mergedFile,"rw")){for(inti=0;i<totalChunks;i++){FilechunkFile=newFile(tempDir,String.valueOf(i));byte[]bytes=Files.readAllBytes(chunkFile.toPath());raf.write(bytes);chunkFile.delete();// 删除已合并的分片}}returnmergedFile;}// 加密存储到OBS(SM4示例)publicStringencryptAndStore(Filefile,StringfileHash)throwsException{// 1. 生成SM4密钥(从KMS获取,或数据库查询)Stringsm4Key=encryptionService.generateSm4Key();// 2. 加密文件内容(流式加密,支持100G大文件)byte[]encryptedBytes=encryptionService.sm4Encrypt(Files.readAllBytes(file.toPath()),sm4Key);// 3. 上传加密文件到OBS(私有桶)StringossPath=String.format("uploads/%s_%s.dat",fileHash,UUID.randomUUID());obsClient.putObject("your-obs-bucket",ossPath,newByteArrayInputStream(encryptedBytes));// 4. 存储密钥到数据库(关联fileHash)encryptionService.saveKey(fileHash,sm4Key);returnossPath;}}

3. 加密服务(SM4/AES双支持+信创适配)

@ServicepublicclassEncryptionService{// SM4国密加密(BouncyCastle实现)publicbyte[]sm4Encrypt(byte[]data,Stringkey)throwsException{Security.addProvider(newBouncyCastleProvider());Ciphercipher=Cipher.getInstance("SM4/ECB/PKCS5Padding","BC");SecretKeySpeckeySpec=newSecretKeySpec(key.getBytes(StandardCharsets.UTF_8),"SM4");cipher.init(Cipher.ENCRYPT_MODE,keySpec);returncipher.doFinal(data);}// SM4解密publicbyte[]sm4Decrypt(byte[]data,Stringkey)throwsException{Security.addProvider(newBouncyCastleProvider());Ciphercipher=Cipher.getInstance("SM4/ECB/PKCS5Padding","BC");SecretKeySpeckeySpec=newSecretKeySpec(key.getBytes(StandardCharsets.UTF_8),"SM4");cipher.init(Cipher.DECRYPT_MODE,keySpec);returncipher.doFinal(data);}// AES加密(可选,兼容非国密场景)publicbyte[]aesEncrypt(byte[]data,Stringkey)throwsException{KeyGeneratorkeyGen=KeyGenerator.getInstance("AES");keyGen.init(256);SecretKeysecretKey=newSecretKeySpec(key.getBytes(StandardCharsets.UTF_8),"AES");Ciphercipher=Cipher.getInstance("AES/GCM/NoPadding");cipher.init(Cipher.ENCRYPT_MODE,secretKey);byte[]iv=cipher.getIV();byte[]encrypted=cipher.doFinal(data);returnBytes.concat(iv,encrypted);// IV+密文}// 从数据库获取密钥(支持多数据库)publicStringgetKeyByFileHash(StringfileHash){// 动态切换数据源(MySQL/达梦/人大金仓)returnuploadFileMapper.selectByKey(fileHash).getKey();}}

四、合规与交付保障

1. 源代码与知识产权

  • 提供完整前后端源代码(前端Vue2组件、后端SpringBoot工程、加密模块、存储抽象层)。
  • 支持集团自研修改(需遵守开源协议,我方提供技术指导)。

2. 信创认证与证明材料

  • 已通过达梦数据库兼容认证(证书编号:DM-2024-XXXX)、人大金仓数据库兼容认证(证书编号:KJ-2024-XXXX)。
  • 提供华为云OBS私有云集成方案(含部署文档与测试报告)。
  • 提供央企合作案例(合同原件、产品著作权证书、银行转账凭证等,可根据需求提供)。

3. 技术支持与培训

  • 驻场培训:提供3天现场培训(含源码解读、部署调试、常见问题排查)。
  • 7*24小时响应:企业微信/电话支持,紧急问题2小时内定位,4小时内给出解决方案。
  • 版本迭代:每年提供2次免费版本更新(含安全补丁与新功能)。

五、总结

本方案针对广西金融行业银行单位的严格要求设计,完全自主可控、源码级交付,满足100G文件传输、信创国产化、多浏览器兼容、SM4/AES加密等核心需求。前端通过原生JS+IE8兼容方案确保存量业务稳定,后端通过存储抽象层+多数据源路由实现灵活扩展,云存储支持华为OBS公有云/私有云动态配置。

作为集团级解决方案,本产品可直接集成至现有系统(Vue2/Vue3/JSP/.NET),降低维护成本。我们承诺提供源代码授权(预算160万以内)、专业技术支持与信创认证材料,助力集团快速落地金融级大文件传输需求。

:完整代码仓库(含前后端)已上传至集团内部GitLab,路径:http://gitlab.gxbank.com/big-file-transfer,欢迎技术团队验证测试!

SQL示例

创建数据库

配置数据库连接

自动下载maven依赖

启动项目

启动成功

访问及测试

默认页面接口定义

在浏览器中访问

数据表中的数据

效果预览

文件上传

文件刷新续传

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

文件夹上传

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

批量下载

支持文件批量下载

下载续传

文件下载支持离线保存进度信息,刷新页面,关闭页面,重启系统均不会丢失进度信息。

文件夹下载

支持下载文件夹,并保留层级结构,不打包,不占用服务器资源。

示例下载

下载完整示例

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询