news 2026/6/9 23:36:23

SpringMVC大文件上传后如何加密存储?

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SpringMVC大文件上传后如何加密存储?

项目技术方案:大文件传输系统(企业级高可用方案)
编制:北京某互联网企业 前端研发部 - 张工
日期:2023年11月28日


一、项目背景与核心痛点

公司承接政府/军工领域项目,需开发50GB级文件传输系统,现有开源方案存在以下问题:

  1. WebUploader:已停更,IE兼容性差(需Flash),无技术支持
  2. Uppy/Plupload:现代浏览器支持好,但IE11需polyfill,无军工级稳定性保障
  3. 商业授权:单套授权成本高(约$5k/年),200+项目部署成本不可控

核心需求

  • 支持50GB文件分片传输(文件夹保留层级结构)
  • 断点续传(跨浏览器会话持久化)
  • 兼容IE11/Chrome/Firefox/360安全浏览器(兼容模式)
  • 私有化部署(内网环境)
  • 提供完整源代码(避免授权风险)

二、技术选型与架构设计

1. 前端架构(Vue3 + 兼容层)

Vue3主应用

文件选择器

分片上传控制器

进度持久化服务

WebDAV文件夹解析

加密传输模块

IndexedDB+Cookie双存储

关键组件

  • 文件夹解析:基于WebDAV协议解析本地文件夹结构(兼容IE11)
  • 分片引擎:动态调整分片大小(5MB-50MB自适应网络)
  • 持久化存储
    • 优先使用IndexedDB(存储分片元数据)
    • 降级使用Cookie(存储关键进度信息,兼容IE11)
2. 后端架构(SpringBoot微服务)
// 文件分片服务接口示例@RestController@RequestMapping("/api/upload")publicclassFileUploadController{@PostMapping("/init")publicResponseEntityinitUpload(@RequestParamStringfileName,@RequestParamlongfileSize,@RequestParamStringrelativePath){// 生成唯一任务ID(UUID v4)StringtaskId=UUID.randomUUID().toString();// 存储元数据到数据库(适配MySQL/Oracle/SQLServer)fileMetaService.save(taskId,fileName,fileSize,relativePath);returnResponseEntity.ok(taskId);}@PostMapping("/chunk")publicResponseEntityuploadChunk(@RequestParamStringtaskId,@RequestParamintchunkIndex,@RequestParamStringchunkHash,@RequestBodybyte[]chunkData){// 校验分片完整性(SHA-256)StringcomputedHash=DigestUtils.sha256Hex(chunkData);if(!computedHash.equals(chunkHash)){thrownewRuntimeException("分片校验失败");}// 存储分片到临时目录PathtempPath=Paths.get("/tmp/upload/"+taskId+"/"+chunkIndex);Files.write(tempPath,chunkData);// 更新数据库分片状态chunkStatusService.markUploaded(taskId,chunkIndex);returnResponseEntity.ok().build();}}

三、核心代码实现

1. 文件夹上传(Vue3组件)
import { ref, onMounted } from 'vue'; import { useIndexedDB } from './useIndexedDB'; // 自定义IndexedDB Hook import { sha256 } from 'js-sha256'; const fileInput = ref(null); const progress = ref(0); const { db, addTask, getTask } = useIndexedDB('fileUploadDB'); // 解析文件夹结构(兼容IE11) const parseFolder = (files) => { const tree = {}; Array.from(files).forEach(file => { const pathParts = file.webkitRelativePath.split('/'); let current = tree; pathParts.forEach((part, index) => { if (index === pathParts.length - 1) { current[part] = { file, path: file.webkitRelativePath, size: file.size }; } else { if (!current[part]) current[part] = {}; current = current[part]; } }); }); return tree; }; // 初始化上传任务 const initUploadTask = async (fileTree) => { const flatFiles = flattenFileTree(fileTree); const taskId = crypto.randomUUID(); // 存储到IndexedDB await addTask({ taskId, files: flatFiles, status: 'pending', createdAt: new Date() }); return taskId; }; // 分片上传逻辑 const uploadChunk = async (taskId, file, chunkIndex, chunkSize) => { const start = chunkIndex * chunkSize; const end = Math.min(file.size, start + chunkSize); const chunk = file.slice(start, end); // 计算分片哈希(用于校验) const chunkHash = sha256(chunk); try { const response = await fetch(`/api/upload/chunk`, { method: 'POST', headers: { 'Content-Type': 'application/octet-stream', 'X-Task-ID': taskId, 'X-Chunk-Index': chunkIndex.toString(), 'X-Chunk-Hash': chunkHash }, body: chunk }); if (!response.ok) throw new Error('上传失败'); // 更新本地进度 const task = await getTask(taskId); task.uploadedChunks.add(chunkIndex); await addTask(task); // 计算总体进度 const totalChunks = Math.ceil(file.size / chunkSize); const fileProgress = (task.uploadedChunks.size / totalChunks) * 100; // 更新全局进度(按文件大小加权) updateGlobalProgress(task, fileProgress); } catch (error) { console.error('分片上传失败:', error); throw error; } };
2. 断点续传持久化(IndexedDB + Cookie)
// useIndexedDB.js - 自定义HookexportfunctionuseIndexedDB(dbName){letdbInstance=null;constinitDB=async()=>{returnnewPromise((resolve,reject)=>{constrequest=indexedDB.open(dbName,2);request.onupgradeneeded=(e)=>{constdb=e.target.result;if(!db.objectStoreNames.contains('tasks')){db.createObjectStore('tasks',{keyPath:'taskId'});}};request.onsuccess=(e)=>{dbInstance=e.target.result;resolve(dbInstance);};request.onerror=(e)=>reject(e.target.error);});};constaddTask=async(task)=>{if(!dbInstance)awaitinitDB();returnnewPromise((resolve,reject)=>{consttx=dbInstance.transaction('tasks','readwrite');conststore=tx.objectStore('tasks');constrequest=store.put(task);request.onsuccess=()=>{// 同步关键信息到Cookie(IE11兼容)setCookie(`upload_task_${task.taskId}`,JSON.stringify({taskId:task.taskId,fileName:task.files[0]?.name,progress:calculateProgress(task)}),30);resolve();};request.onerror=reject;});};return{db:dbInstance,addTask,getTask};}// Cookie兼容层(IE11)functionsetCookie(name,value,days){letexpires='';if(days){constdate=newDate();date.setTime(date.getTime()+(days*24*60*60*1000));expires=`; expires=${date.toUTCString()}`;}document.cookie=`${name}=${value||''}${expires}; path=/`;}

四、兼容性解决方案

1. 浏览器兼容性矩阵
浏览器最低版本关键兼容方案
Chrome80+原生支持
Firefox78+原生支持
IE1111使用IndexedDB polyfill + Flash回退
360安全浏览器10+兼容模式识别(User-Agent检测)
2. IE11降级方案

五、部署与维护方案

  1. 私有化部署包

    • 提供Docker镜像(多架构支持)
    • 包含数据库初始化脚本(MySQL/Oracle/SQLServer)
  2. 维护策略

    • 建立内部维护团队(3人核心组)
    • 每月进行压力测试(50GB文件传输稳定性验证)
  3. 成本估算

    • 自研成本:约15人月(含测试)
    • 长期维护成本:每年约20万元(人力+服务器)

下一步行动建议

  1. 组建专项小组(前端2人 + 后端1人 + 测试1人)
  2. 采购基础版商业组件(如Plupload企业版)作为技术参考
  3. 搭建测试环境(IE11 + Windows Server 2016)

(签名:北京某互联网企业 前端架构师 张工)
联系方式:zhang.gong@company.com | 延伸阅读:《大文件传输系统性能优化白皮书》

导入项目

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

工程

NOSQL

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

创建数据表

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

修改数据库连接信息

访问页面进行测试

文件存储路径

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

效果预览

文件上传

文件刷新续传

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

文件夹上传

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

下载示例

点击下载完整示例

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

优思学院|产能过剩、牛鞭效应与精益管理

当一个国家或一个产业的生产能力大大超过市场真实需求时,经济体系中往往会出现一个非常直观却又极具破坏力的现象:产品卖不出去,库存越堆越高,设备、人力与资本被迫闲置。短期看,这只是企业经营层面的困难;…

作者头像 李华
网站建设 2026/6/5 20:36:47

Sora Video2+一步API进阶实战:核心高级功能完整实现

【前言】在实际项目落地中,开发者往往会面临参考视频导入、数字人分身、异步回调等高级功能落地难的问题。本文作为进阶实战上篇,将聚焦3大核心高级功能的完整实现方案,提供可直接复用的代码示例、参数说明与注意事项,助力开发者快…

作者头像 李华
网站建设 2026/6/6 8:28:43

编程语言排行榜哪个更权威?解读Python等语言排名

了解编程语言排行榜对开发者选择技术栈、评估行业趋势至关重要。排行榜并非绝对权威,但能反映语言在社区活跃度、就业市场需求和技术生态等方面的综合表现。我将从实际应用角度分析几个主流榜单的参考价值,帮助大家更理性地利用这些数据。 编程语言排行…

作者头像 李华
网站建设 2026/6/8 12:15:09

不用写代码!1个小时就能搭建出专属网站,可能吗?

“定制一个网站,没有半个月时间、不花上万块钱,肯定搞不定。”——这是很多人的固有印象。但今天,答案可以是肯定的:一个小时内搭建一个功能齐全的专属网站,是完全可能的。关键在于你是否选对了工具,以及你…

作者头像 李华
网站建设 2026/6/6 7:49:37

计算机毕业设计 java 游戏账号交易平台 基于 SpringBoot 的游戏账号安全交易平台 Java 游戏账号交易与资讯交流系统

计算机毕业设计 java 游戏账号交易平台 54w649(配套有源码 程序 mysql 数据库 论文)本套源码可以先看具体功能演示视频领取,文末有联系 可分享随着游戏产业的快速发展,游戏账号交易需求日益旺盛,但传统交易模式存在安全…

作者头像 李华
网站建设 2026/6/5 8:42:41

计算机毕业设计springboot社区服务平台的设计与实现 基于SpringBoot的社区服务综合管理系统的设计与实现 智慧社区便民服务平台的构建与实现

计算机毕业设计springboot社区服务平台的设计与实现ah2z44z4 (配套有源码 程序 mysql数据库 论文) 本套源码可以在文本联xi,先看具体系统功能演示视频领取,可分享源码参考。随着信息技术的迅猛发展,城市化进程不断加速&#xff0c…

作者头像 李华