news 2026/4/23 2:33:51

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/4/21 10:58:16

GraphQL注入攻击模式:解剖现代API的注入风险

第一部分:开篇明义 —— 定义、价值与目标 定位与价值 在从传统单体架构向微服务、前后端分离演进的浪潮中,GraphQL 作为一种强大的API查询语言,因其精确获取数据和减少网络请求的能力,迅速成为构建现代Web与移动应用的首选方案…

作者头像 李华
网站建设 2026/4/21 22:25:45

开发闲鱼卖货标题优化工具,输入商品名称,自动生成三组高爆光标题,(含热搜索关键词,商品卖点),适配闲鱼搜索规则,提升商品浏览量。

1. 实际应用场景描述场景:小张是一名闲鱼卖家,主要出售二手数码产品、家居用品等。他发现,闲鱼的搜索流量很大,但标题如果没写好,即使商品再好也无人问津。很多高曝光的商品标题都包含热搜关键词 卖点 场景词&#x…

作者头像 李华
网站建设 2026/4/15 14:28:37

【同步挤压变换和重新分配方法】【时频分析(TFA)】解决海森堡不确定性原理(HUP)提供新的方法附Matlab代码

✅作者简介:热爱科研的Matlab仿真开发者,擅长数据处理、建模仿真、程序设计、完整代码获取、论文复现及科研仿真。🍎 往期回顾关注个人主页:Matlab科研工作室🍊个人信条:格物致知,完整Matlab代码及仿真咨询…

作者头像 李华
网站建设 2026/4/22 15:44:12

2024金融AI智能体投资决策的技术趋势:架构师的预判与布局

2024金融AI智能体投资决策技术趋势:架构师的预判与布局 副标题:从技术演进到业务落地的实战指南 摘要/引言 问题陈述 传统金融投资决策系统面临三大核心挑战: 规则僵化:依赖人工制定的规则引擎,无法适应市场环境的…

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

2026养老新政全方位落地,银发生活迎来新机遇!

2026年伊始,养老新政全面落地,从保障体系、服务供给、科技赋能到产业生态实现系统性升级。 既为 3 亿老年人筑牢幸福底线,也为银发经济开辟万亿级新蓝海。以下从政策核心、生活变革、产业机遇三方面展开深度解读。 一、新政核心:…

作者头像 李华