news 2026/4/23 14:22:22

SpringBoot大附件上传的加密存储原理与思路分享

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SpringBoot大附件上传的加密存储原理与思路分享

大文件传输系统技术调研与解决方案设计

一、项目背景与需求分析

作为江苏某软件公司的前端工程师,我目前负责一个需要支持20GB级大文件传输的项目。该项目需要实现以下核心功能:

  1. 支持单文件和文件夹的上传下载(文件夹需保留完整层级结构)
  2. 兼容主流浏览器及信创国产浏览器(龙芯、红莲花、奇安信等)
  3. 支持信创国产化环境(统信UOS、中标麒麟、银河麒麟)
  4. 数据库兼容主流及国产化产品(SQL Server/MySQL/Oracle/达梦/人大金仓)
  5. 后端基于SpringBoot,前端基于Vue2-cli框架

二、现有方案评估

2.1 WebUploader问题分析

之前使用的百度WebUploader组件存在以下问题:

  • 已停止维护,无技术支持
  • 在大文件传输时经常出现数据错误
  • 对国产浏览器兼容性不足
  • 分片上传机制在极端网络条件下不稳定

2.2 其他开源方案调研

调研了市面上主流开源组件(如Uppy、Plupload、Resumable.js等),发现普遍存在:

  • 缺乏对国产软硬件环境的全面支持
  • 文档不完善,二次开发成本高
  • 无商业技术支持,问题响应慢

三、技术方案设计

3.1 整体架构设计

前端(Vue2) ↔ 网关层 ↔ 后端(SpringBoot) ↔ 存储层 ↑ ↑ 浏览器适配 数据库适配

3.2 前端实现方案

3.2.1 文件选择与预处理
// file-selector.vue 组件核心代码methods:{asynchandleFolderSelect(){try{// 使用webkitdirectory属性实现文件夹选择(兼容性处理)constinput=document.createElement('input');input.type='file';input.webkitDirectory=true;// Chrome/Edge等input.directory=true;// Firefox备用input.onchange=async(e)=>{constfiles=Array.from(e.target.files);if(files.length===0)return;// 构建文件树结构constfileTree=this.buildFileTree(files);this.$emit('file-tree-ready',fileTree);};// 国产浏览器兼容处理if(this.isXinChuangBrowser()){// 调用浏览器扩展API或使用ActiveX(根据具体浏览器)this.selectFilesInXinChuangBrowser();}else{input.click();}}catch(error){console.error('文件夹选择失败:',error);this.$message.error('文件夹选择失败,请尝试使用普通文件上传');}},buildFileTree(files){consttree={};files.forEach(file=>{constpathParts=file.webkitRelativePath.split('/');letcurrentLevel=tree;pathParts.forEach((part,index)=>{if(!currentLevel[part]){currentLevel[part]=index===pathParts.length-1?file:{};}currentLevel=currentLevel[part];});});returntree;}}
3.2.2 分片上传实现
// chunk-uploader.js 核心逻辑classChunkUploader{constructor(file,options){this.file=file;this.chunkSize=options.chunkSize||5*1024*1024;// 默认5MBthis.concurrent=options.concurrent||3;this.fileId=this.generateFileId();this.uploadedChunks=newSet();}asyncstartUpload(url){consttotalChunks=Math.ceil(this.file.size/this.chunkSize);constpromises=[];// 使用Promise.all实现并发控制for(leti=0;i<this.concurrent;i++){constworker=async()=>{for(letchunkIndex=i;chunkIndex<totalChunks;chunkIndex+=this.concurrent){if(this.uploadedChunks.has(chunkIndex))continue;try{awaitthis.uploadChunk(url,chunkIndex,totalChunks);this.uploadedChunks.add(chunkIndex);}catch(error){console.error(`分片${chunkIndex}上传失败`,error);// 实现失败重试机制chunkIndex--;}}};promises.push(worker());}awaitPromise.all(promises);awaitthis.mergeChunks(url,totalChunks);}asyncuploadChunk(url,chunkIndex,totalChunks){conststart=chunkIndex*this.chunkSize;constend=Math.min(start+this.chunkSize,this.file.size);constchunk=this.file.slice(start,end);constformData=newFormData();formData.append('file',chunk);formData.append('fileId',this.fileId);formData.append('chunkIndex',chunkIndex);formData.append('totalChunks',totalChunks);formData.append('fileName',this.file.name);formData.append('relativePath',this.relativePath||'');returnfetch(url,{method:'POST',body:formData,headers:{// 国产浏览器可能需要特殊header'X-Requested-With':'XMLHttpRequest'}});}}

3.3 后端实现方案

3.3.1 SpringBoot控制器
@RestController@RequestMapping("/api/file")publicclassFileTransferController{@AutowiredprivateFileChunkServicechunkService;@AutowiredprivateFileMergeServicemergeService;@PostMapping("/upload")publicResponseEntityuploadChunk(@RequestParam("file")MultipartFilefile,@RequestParam("fileId")StringfileId,@RequestParam("chunkIndex")intchunkIndex,@RequestParam("totalChunks")inttotalChunks,@RequestParam(value="relativePath",required=false)StringrelativePath){try{chunkService.saveChunk(fileId,chunkIndex,file.getBytes());// 检查是否所有分片都已上传if(chunkService.isAllChunksUploaded(fileId,totalChunks)){StringfilePath=mergeService.mergeFile(fileId,totalChunks,relativePath);returnResponseEntity.ok(newUploadResponse(true,filePath));}returnResponseEntity.ok(newUploadResponse(false,null));}catch(IOExceptione){returnResponseEntity.internalServerError().build();}}@GetMapping("/download")publicResponseEntitydownloadFile(@RequestParamStringfilePath,HttpServletRequestrequest){try{Resourceresource=fileStorageService.loadFileAsResource(filePath);// 处理国产浏览器下载问题StringuserAgent=request.getHeader("User-Agent");if(isXinChuangBrowser(userAgent)){returnResponseEntity.ok().header(HttpHeaders.CONTENT_DISPOSITION,"attachment; filename=\""+resource.getFilename()+"\"").body(resource);}returnResponseEntity.ok().header(HttpHeaders.CONTENT_DISPOSITION,"attachment; filename=\""+URLEncoder.encode(resource.getFilename(),"UTF-8")+"\"").body(resource);}catch(Exceptione){returnResponseEntity.notFound().build();}}}
3.3.2 分片存储服务
@ServicepublicclassFileChunkServiceImplimplementsFileChunkService{@Value("${file.upload-dir}")privateStringuploadDir;@OverridepublicvoidsaveChunk(StringfileId,intchunkIndex,byte[]chunkData)throwsIOException{PathchunkPath=Paths.get(uploadDir,fileId,String.valueOf(chunkIndex));Files.createDirectories(chunkPath.getParent());Files.write(chunkPath,chunkData);}@OverridepublicbooleanisAllChunksUploaded(StringfileId,inttotalChunks){PathchunkDir=Paths.get(uploadDir,fileId);if(!Files.exists(chunkDir)){returnfalse;}try(DirectoryStreamstream=Files.newDirectoryStream(chunkDir)){returnstream.spliterator().getExactSizeIfKnown()==totalChunks;}catch(IOExceptione){returnfalse;}}}

3.4 数据库适配方案

3.4.1 抽象数据访问层
publicinterfaceFileMetadataRepository{voidsave(FileMetadatametadata);FileMetadatafindByFileId(StringfileId);// 其他CRUD方法...}@Repository@ConditionalOnProperty(name="db.type",havingValue="mysql")publicclassMySQLFileMetadataRepositoryimplementsFileMetadataRepository{@AutowiredprivateJdbcTemplatejdbcTemplate;@Overridepublicvoidsave(FileMetadatametadata){Stringsql="INSERT INTO file_metadata (file_id, file_name, relative_path, total_size, ...) "+"VALUES (?, ?, ?, ?, ...)";jdbcTemplate.update(sql,metadata.getFileId(),metadata.getFileName(),...);}}@Repository@ConditionalOnProperty(name="db.type",havingValue="dameng")publicclassDamengFileMetadataRepositoryimplementsFileMetadataRepository{// 达梦数据库特定实现}

四、关键问题解决方案

4.1 国产浏览器兼容性处理

  1. 文件夹选择
  • 使用webkitdirectory属性作为主要方案
  • 为龙芯/红莲花等浏览器开发ActiveX控件或NPAPI插件(需浏览器支持)
  • 提供备用上传方式(ZIP压缩包上传)
  1. 文件下载
  • 检测User-Agent,对国产浏览器使用特殊处理
  • 实现服务端文件名编码转换

4.2 大文件传输稳定性优化

  1. 断点续传
  • 前端记录已上传分片
  • 后端提供分片校验接口
  1. 网络异常处理
  • 实现指数退避重试机制
  • 提供上传进度实时反馈
  1. 内存优化
  • 使用流式处理避免大文件全量加载到内存
  • 限制并发上传数

4.3 信创环境适配

  1. 操作系统适配
  • 使用SpringBoot的Profile功能区分不同环境配置
  • 测试阶段覆盖统信UOS、中标麒麟、银河麒麟
  1. 数据库适配
  • 通过Spring Data JPA实现基础接口统一
  • 各数据库实现类处理方言差异

五、实施计划

  1. 第一阶段(2周)
  • 完成核心组件原型开发
  • 实现基本文件上传下载功能
  • 搭建信创环境测试基础
  1. 第二阶段(3周)
  • 完善文件夹上传功能
  • 实现断点续传和进度显示
  • 完成国产浏览器兼容性适配
  1. 第三阶段(2周)
  • 数据库适配层开发
  • 性能优化和压力测试
  • 编写详细技术文档

六、风险评估与应对

  1. 国产浏览器兼容性风险
  • 应对:与浏览器厂商建立联系,获取技术文档支持
  1. 大文件传输性能风险
  • 应对:实施分阶段压力测试,逐步优化传输参数
  1. 信创环境差异风险
  • 应对:提前准备多套测试环境,建立快速反馈机制

该方案结合了现有开源组件的优点,同时针对项目特殊需求进行了定制化开发,特别是在国产软硬件适配方面做了充分考虑。建议后续开发过程中建立持续集成环境,覆盖主流和信创环境的自动化测试。

导入项目

导入到Eclipse:点南查看教程
导入到IDEA:点击查看教程
springboot统一配置:点击查看教程

工程

NOSQL

NOSQL示例不需要任何配置,可以直接访问测试

创建数据表

选择对应的数据表脚本,这里以SQL为例

修改数据库连接信息

访问页面进行测试

文件存储路径

up6/upload/年/月/日/guid/filename

效果预览

文件上传

文件刷新续传

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

文件夹上传

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

下载示例

点击下载完整示例

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

噪声污染分布:GLM-4.6V-Flash-WEB关联街景与声学传感器

噪声污染分布&#xff1a;GLM-4.6V-Flash-WEB关联街景与声学传感器 在一座超大城市中&#xff0c;每天有数以万计的车辆穿行于高架桥与主干道之间&#xff0c;建筑工地昼夜施工&#xff0c;地铁线路延伸不断。这些动态变化带来的噪声&#xff0c;并非固定不变——它随时间、天气…

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

GLM-4.6V-Flash-WEB模型在法律文书图像识别中的潜力

GLM-4.6V-Flash-WEB模型在法律文书图像识别中的潜力多模态AI如何破解法律文档处理困局&#xff1f; 在法院档案室里&#xff0c;一位书记员正面对一叠泛黄的判决书扫描件发愁&#xff1a;手写批注与印刷文字交错、表格边框模糊、关键条款被装订线遮挡……即便用上了OCR工具&…

作者头像 李华
网站建设 2026/4/23 11:26:44

[Windows] U盘扩容检测工具 ValiDrive v1.0.1

[Windows] U盘扩容检测工具 ValiDrive v1.0.1 链接&#xff1a;https://pan.xunlei.com/s/VOiCnkI10uVZCW5AfyBFLvYbA1?pwdr2pi# ValiDrive 是一款由 Gibson Research 开发的免费工具&#xff0c;专门用于检测 U 盘、移动硬盘等 USB 存储设备的真实容量、读写性能和可靠性&a…

作者头像 李华
网站建设 2026/4/23 14:13:30

GLM-4.6V-Flash-WEB模型对细节元素识别能力的专项测试

GLM-4.6V-Flash-WEB模型对细节元素识别能力的专项测试 在今天这个图像信息爆炸的时代&#xff0c;用户每天面对成千上万的界面截图、操作提示和视觉控件。无论是客服系统自动解析用户上传的问题截图&#xff0c;还是无障碍工具为视障人士实时描述屏幕内容&#xff0c;背后都依赖…

作者头像 李华
网站建设 2026/4/23 12:48:41

为什么GLM-4.6V-Flash-WEB成为轻量化多模态应用首选?

为什么GLM-4.6V-Flash-WEB成为轻量化多模态应用首选&#xff1f; 在智能应用日益“视觉化”的今天&#xff0c;用户不再满足于纯文本交互。一张截图、一段带图的工单、一份财报图表——这些非结构化视觉信息正迅速成为人机沟通的新语言。但问题也随之而来&#xff1a;传统多模…

作者头像 李华
网站建设 2026/4/18 7:35:48

GLM-4.6V-Flash-WEB模型技术亮点解析:高效、精准、可落地

GLM-4.6V-Flash-WEB模型技术亮点解析&#xff1a;高效、精准、可落地 在今天这个AI无处不在的时代&#xff0c;一个真正“能用”的模型&#xff0c;早已不单看它懂多少知识、答得多聪明——更关键的是&#xff0c;它能不能快速响应、低成本部署、开箱即用。尤其是在Web服务、在…

作者头像 李华