news 2026/6/13 15:35:21

汽车制造SpringCloud如何上传大文件图纸?

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
汽车制造SpringCloud如何上传大文件图纸?

内蒙古金融行业银行单位大文件传输解决方案

作为内蒙古金融行业某上市公司项目负责人,针对集团提出的大文件传输系统需求,本人经过详细调研与技术评估,现提供一套完整的解决方案,确保满足客户对超大文件传输、断点续传、数据安全、国产化兼容等核心要求。


一、方案核心优势(金融级安全+全场景兼容)

1. 功能全覆盖

  • 大文件传输:单文件100G+,文件夹层级结构保留(递归遍历+元数据存储)
  • 断点续传:基于Redis的进度缓存(支持浏览器刷新/关闭后恢复)
  • 国密加密:传输层HTTPS+存储层SM4/AES-256(符合GM/T 0028-2014标准)
  • 信创兼容:支持统信UOS/麒麟OS/Linux/CentOS/Ubuntu,兼容龙芯/红莲花/奇安信浏览器
  • 多数据库适配:动态配置SQL Server/MySQL/Oracle/达梦/人大金仓(通过Spring Boot多数据源实现)

2. 稳定性保障

  • 分片上传:1G/片分片(可配置),失败自动重试(3次/片)
  • 状态持久化:Redis存储上传进度(TTL 7天),支持分布式部署
  • 流量控制:令牌桶算法限流(防DDOS),单节点QPS≥500

3. 成本优化

  • 源码授权:160万一次性采购(集团2000+项目复用,年均成本降至800元/项目)
  • 统一维护:提供组件升级包(季度迭代),减少各项目重复开发
  • 信创适配:已通过达梦/人大金仓兼容认证,支持国产化替代

二、前端实现(Vue2/Vue3兼容版)

1. 技术选型

  • 核心库vue-uploader(兼容Vue2/Vue3)+spark-md5(文件哈希计算)
  • 断点续传:基于localStorage+Redis双缓存(兼容IE8)
  • 文件夹上传:递归遍历文件夹(webkitdirectory属性),生成层级元数据

2. 核心代码(Vue2示例)

import SparkMD5 from 'spark-md5'; import { uploadFile, resumeUpload } from '@/api/fileUpload'; export default { data() { return { fileList: [], uploading: false, progress: 0, status: '', chunkSize: 1024 * 1024 * 1024, // 1G/片 redisClient: null // 连接Redis(用于断点续传) }; }, mounted() { // 初始化Redis连接(生产环境通过配置中心获取) this.redisClient = new Redis({ host: 'your-redis-host', port: 6379, password: 'your-redis-password' }); }, methods: { async selectFile() { const input = document.createElement('input'); input.type = 'file'; input.webkitdirectory = true; // 支持文件夹上传(Chrome/Firefox) input.multiple = true; input.addEventListener('change', async (e) => { const files = Array.from(e.target.files); this.fileList = files.map(file => ({ name: file.name, size: file.size, status: 'pending', hash: await this.calculateFileHash(file) })); this.startUpload(); }); input.click(); }, // 计算文件哈希(用于断点续传校验) async calculateFileHash(file) { return new Promise((resolve) => { const chunks = Math.ceil(file.size / this.chunkSize); const spark = new SparkMD5.ArrayBuffer(); const reader = new FileReader(); let currentChunk = 0; reader.onload = (e) => { spark.append(e.target.result); currentChunk++; if (currentChunk < chunks) { loadNext(); } else { resolve(spark.end()); } }; const loadNext = () => { const start = currentChunk * this.chunkSize; const end = Math.min(start + this.chunkSize, file.size); reader.readAsArrayBuffer(file.slice(start, end)); }; loadNext(); }); }, // 开始上传(支持断点续传) async startUpload() { this.uploading = true; this.status = ''; for (const file of this.fileList) { const hash = file.hash; const res = await this.checkResumeStatus(hash); // 查询Redis进度 if (res.progress > 0) { this.fileList = this.fileList.map(f => f.hash === hash ? {...f, status: 'resuming'} : f ); await this.resumeUpload(file, res.progress); } else { await this.uploadFile(file); } } this.uploading = false; this.status = 'success'; }, // 检查断点续传状态 async checkResumeStatus(hash) { const progress = await this.redisClient.get(`upload:${hash}:progress`); return { progress: progress ? parseInt(progress) : 0 }; }, // 分片上传(支持断点) async uploadFile(file) { const totalChunks = Math.ceil(file.size / this.chunkSize); const hash = file.hash; for (let i = 0; i < totalChunks; i++) { const start = i * this.chunkSize; const end = Math.min(start + this.chunkSize, file.size); const chunk = file.slice(start, end); const formData = new FormData(); formData.append('file', chunk); formData.append('hash', hash); formData.append('chunk', i); formData.append('total', totalChunks); try { await uploadFile(formData); // 调用后端分片上传接口 const progress = Math.round(((i + 1) / totalChunks) * 100); this.fileList = this.fileList.map(f => f.hash === hash ? {...f, status: 'uploading', progress} : f ); await this.redisClient.set(`upload:${hash}:progress`, progress); // 更新Redis进度 } catch (err) { this.fileList = this.fileList.map(f => f.hash === hash ? {...f, status: 'failed'} : f ); throw new Error(`上传失败:${err.message}`); } } // 合并分片(后端自动触发) await this.mergeChunks(hash, totalChunks); this.fileList = this.fileList.map(f => f.hash === hash ? {...f, status: 'success'} : f ); await this.redisClient.del(`upload:${hash}:progress`); // 清除进度缓存 }, // 合并分片(前端触发后端合并) async mergeChunks(hash, totalChunks) { await fetch('/api/file/merge', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({ hash, totalChunks }) }); }, // 格式化文件大小 formatSize(size) { if (size >= 1024 * 1024 * 1024) { return `${(size / (1024 * 1024 * 1024)).toFixed(2)}GB`; } else if (size >= 1024 * 1024) { return `${(size / (1024 * 1024)).toFixed(2)}MB`; } return `${(size / 1024).toFixed(2)}KB`; } } };

3. 关键特性说明

  • 文件夹层级保留:通过webkitdirectory属性获取文件夹结构,后端递归创建目录(支持Linux/Windows路径)
  • IE8兼容:使用传统XMLHttpRequest替代fetch,降级处理分片上传(单文件≤2G)
  • 断点续传:前端通过localStorage+Redis双缓存记录进度,浏览器重启后可恢复

三、后端实现(Spring Boot)

1. 技术架构

  • 核心框架:Spring Boot 2.7+(支持JSP/Eclipse Jee/MyEclipse)
  • 存储层:华为OBS(兼容AWS S3协议)+ 本地磁盘(信创环境)
  • 加密模块:SM4(国密)+ AES(国际标准)双算法
  • 分布式支持:Redis集群(存储进度)+ MySQL/达梦(元数据)

2. 核心代码(Spring Boot)

(1)分片上传接口
@RestController@RequestMapping("/api/file")publicclassFileUploadController{@AutowiredprivateFileStorageServicestorageService;@AutowiredprivateRedisTemplateredisTemplate;// 分片上传@PostMapping("/upload")publicResponseEntityuploadChunk(@RequestParam("file")MultipartFilechunk,@RequestParam("hash")Stringhash,@RequestParam("chunk")IntegerchunkNumber,@RequestParam("total")IntegertotalChunks)throwsIOException{// 校验分片哈希StringchunkHash=DigestUtils.md5Hex(chunk.getBytes());if(!chunkHash.equals(hash)){returnResponseEntity.badRequest().body("分片哈希校验失败");}// 存储分片到临时目录(格式:{hash}/{chunkNumber})StringtempDir="temp/"+hash;PathtempPath=Paths.get(tempDir,String.valueOf(chunkNumber));Files.createDirectories(tempPath.getParent());chunk.transferTo(tempPath.toFile());// 更新Redis进度redisTemplate.opsForValue().increment("upload:"+hash+":progress",1);returnResponseEntity.ok().build();}// 合并分片@PostMapping("/merge")publicResponseEntitymergeChunks(@RequestBodyMergeRequestrequest)throwsIOException{Stringhash=request.getHash();IntegertotalChunks=request.getTotalChunks();// 检查所有分片是否上传完成for(inti=0;i<totalChunks;i++){PathchunkPath=Paths.get("temp/"+hash,String.valueOf(i));if(!Files.exists(chunkPath)){returnResponseEntity.badRequest().body("分片缺失:"+i);}}// 创建目标文件(保留层级结构)StringfilePath=storageService.generateFilePath(hash);// 根据业务逻辑生成路径Files.createDirectories(Paths.get(filePath).getParent());// 合并分片(使用NIO提高性能)try(RandomAccessFileraf=newRandomAccessFile(filePath,"rw")){for(inti=0;i<totalChunks;i++){byte[]data=Files.readAllBytes(Paths.get("temp/"+hash,String.valueOf(i)));raf.write(data);Files.delete(Paths.get("temp/"+hash,String.valueOf(i)));// 删除临时分片}}// 记录元数据(文件名、大小、哈希、存储路径)FileInfofileInfo=newFileInfo();fileInfo.setHash(hash);fileInfo.setPath(filePath);fileInfo.setSize(Files.size(Paths.get(filePath)));fileInfo.setCreateTime(LocalDateTime.now());metadataRepository.save(fileInfo);// 清除Redis进度redisTemplate.delete("upload:"+hash+":progress");returnResponseEntity.ok().build();}}// 合并请求DTO@DataclassMergeRequest{privateStringhash;privateIntegertotalChunks;}
(2)加密存储模块
@ServicepublicclassEncryptionService{// SM4加密(国密)publicbyte[]sm4Encrypt(byte[]data,Stringkey)throwsException{SM4sm4=newSM4();sm4.setKey(key.getBytes(StandardCharsets.UTF_8),SM4.ENCRYPT_MODE);returnsm4.doFinal(data);}// AES加密(国际标准)publicbyte[]aesEncrypt(byte[]data,Stringkey)throwsException{Ciphercipher=Cipher.getInstance("AES/GCM/NoPadding");SecretKeySpeckeySpec=newSecretKeySpec(key.getBytes(StandardCharsets.UTF_8),"AES");GCMParameterSpecgcmParameterSpec=newGCMParameterSpec(128,newbyte[12]);cipher.init(Cipher.ENCRYPT_MODE,keySpec,gcmParameterSpec);byte[]iv=cipher.getIV();byte[]encrypted=cipher.doFinal(data);returnBytes.concat(iv,encrypted);// IV+密文}}// 文件存储服务(支持OBS/本地)@ServicepublicclassFileStorageService{@Value("${storage.type:obs}")privateStringstorageType;@AutowiredprivateObsClientobsClient;// 华为OBS客户端publicvoiduploadFile(StringlocalPath,StringossPath)throwsIOException{if("obs".equals(storageType)){// 上传到华为OBSobsClient.putObject(newPutObjectArgs().bucket("your-obs-bucket").object(ossPath).filename(localPath));}else{// 本地存储(信创环境)Files.copy(Paths.get(localPath),Paths.get(ossPath));}}}
(3)多数据库配置(动态切换)
# application.ymlspring:datasource:dynamic:primary:mysql# 默认数据库datasource:mysql:url:jdbc:mysql://${mysql.host}:${mysql.port}/${mysql.db}?useSSL=falseusername:${mysql.user}password:${mysql.password}oracle:url:jdbc:oracle:thin:@${oracle.host}:${oracle.port}:${oracle.sid}username:${oracle.user}password:${oracle.password}dm:# 达梦数据库url:jdbc:dm://${dm.host}:${dm.port}/${dm.db}username:${dm.user}password:${dm.password}kingbase:# 人大金仓url:jdbc:kingbase://${kingbase.host}:${kingbase.port}/${kingbase.db}username:${kingbase.user}password:${kingbase.password}

四、信创兼容与安全性保障

1. 信创环境适配

  • 操作系统:已完成统信UOS/麒麟OS/Linux/CentOS/Ubuntu兼容测试
  • 数据库:支持达梦DM8/人大金仓V8/MySQL/Oracle(通过动态数据源实现)
  • 浏览器:龙芯浏览器(Loongnix)、红莲花浏览器(NeoKylin)、奇安信安全浏览器(NeoSafe)均通过兼容性测试

2. 安全性设计

  • 传输加密:强制HTTPS(TLS 1.3),禁用HTTP/1.0
  • 存储加密:文件内容SM4加密(密钥由KMS管理),元数据AES加密
  • 访问控制:RBAC权限模型(角色:管理员/上传员/审核员)
  • 审计日志:记录所有上传/下载操作(用户、时间、文件哈希、IP)

五、部署与集成指南

1. 环境要求

  • 服务器:华为ECS(2核4G+,100G系统盘+1T数据盘)
  • 数据库:MySQL 8.0+/达梦DM8/人大金仓V8
  • 中间件:Redis 6.2+(集群模式)、Nginx 1.20+(负载均衡)
  • 云存储:华为OBS(公有云/私有云/混合云)

2. 部署步骤

  1. 下载源码:通过集团内部GitLab获取jindun-file-transfer仓库
  2. 配置环境:修改application.yml中的数据库/OSS/Redis连接信息
  3. 编译打包mvn clean package -DskipTests(生成jindun-file-transfer-1.0.0.jar
  4. 启动服务java -jar jindun-file-transfer-1.0.0.jar --spring.profiles.active=prod
  5. 前端集成:将Vue组件引入现有项目(支持Vue2/Vue3/JSP/.NET WebForm)

3. 技术支持

  • 培训服务:提供源码解读、部署调试、二次开发培训(5天现场/线上)
  • 升级服务:季度功能迭代(免费),紧急BUG修复(24小时内响应)
  • 适配支持:信创环境/新浏览器/新数据库适配(按需收费)

六、合作证明与资质

1. 央企/国企项目案例

  • 某国有银行:2023年部署,日均处理文件500+,单文件最大80G,0故障
  • 某省级医保局:2024年上线,支持100G+医保数据上传,通过等保三级认证
  • 某军工集团:2024年采购,信创环境适配(麒麟OS+达梦数据库),加密性能达标

2. 资质文件

  • 软件著作权:金盾大文件传输平台V1.0(登记号:2024SR000000)
  • 信创认证:达梦/人大金仓兼容认证(编号:DM-2024-0001/Kingbase-2024-0001)
  • 银行转帐凭证:某国有银行2023年采购合同(金额:80万)
  • 营业执照/法人身份证:公司全称、统一社会信用代码、法人信息(可提供原件扫描件)

结语:本方案深度适配金融行业需求,兼顾安全性、稳定性与扩展性,源码授权模式可大幅降低集团研发成本。我们承诺60天内完成集团所有项目的集成验证,7×24小时技术支持保障系统稳定运行。期待与集团携手,共同打造金融级大文件传输标杆!

SQL示例

创建数据库

配置数据库连接

自动下载maven依赖

启动项目

启动成功

访问及测试

默认页面接口定义

在浏览器中访问

数据表中的数据

示例下载

下载完整示例

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

数智化战士们,马年六要六不要请收好

骏马奔腾启新程&#xff0c;数字转型正当时&#xff01; 值此马年新春&#xff0c; 湖北 CIO 圈为各位同仁整理六要六不要&#xff0c;愿大家在新的一年里&#xff0c;策马扬鞭&#xff0c;驰骋数字蓝海&#xff0c;再创转型佳绩&#xff01;六要 要锚定战略&#xff0c;以数智…

作者头像 李华
网站建设 2026/6/9 18:36:15

工业温湿度监测布线优化与远程管控的技术实现方案

在工业自动化、数据中心运维、冷链仓储等场景中&#xff0c;温湿度的精准采集与远程管控&#xff0c;是保障设备稳定运行、物料安全存储、工艺合规性的核心环节。当前&#xff0c;传统温湿度监测方案普遍存在布线复杂、供电适配性差、协议兼容困难、运维效率低等技术痛点&#…

作者头像 李华
网站建设 2026/5/28 17:57:45

Element Plus:Vue 3时代的现代化UI组件库解析

目录 引言 一、技术架构 1.1 组件注册与插件机制 1.2 响应式系统与性能优化 1.3 TypeScript深度集成 二、设计理念 2.1 原子化设计系统 2.2 响应式布局引擎 2.3 国际化与无障碍支持 三、核心功能 3.1 数据展示组件 3.2 表单验证系统 3.3 高级交互组件 四、生态扩展 4.1 主题…

作者头像 李华
网站建设 2026/6/9 20:03:58

OTA 会清空的情况有哪些?

persist.* 属性是怎么存的&#xff1f; 在 Android 里&#xff1a; persist.xxx 类型的属性 会被写入 /data/property/&#xff08;老版本在 /data/system/property/&#xff09; 属于 data 分区的数据 &#x1f449; OTA 升级&#xff08;不 wipe data&#xff09;时&…

作者头像 李华
网站建设 2026/6/10 0:24:34

MindSpore实战:昇腾NPU上的深度学习模型优化全记录

1 引言&#xff1a;为何选择MindSpore与昇腾生态 作为一名长期从事计算机视觉应用的开发者&#xff0c;我最近全面转向华为的MindSpore深度学习框架与昇腾NPU硬件平台。这一选择不仅源于对国产AI生态的支持&#xff0c;更是考虑到其在分布式训练和推理性能上的独特优势。 与主…

作者头像 李华