信创背景下JAVA大文件上传的安全与效率如何保障?

项目技术方案:大文件传输系统(信创兼容版)
编制:湖南长沙某软件公司 技术部
日期:2023年11月20日

一、项目背景与需求分析

公司承接政府项目,需开发一套支持50G文件传输的系统,核心需求如下:

  1. 功能需求
    • 支持单文件/文件夹上传(保留层级结构)及下载
    • 断点续传(刷新/关闭浏览器后恢复进度)
    • 高稳定性(政府项目对数据完整性要求极高)
  2. 兼容性需求
    • 浏览器:IE8+、龙芯浏览器、红莲花浏览器、奇安信安全浏览器
    • 操作系统:统信UOS、中标麒麟、银河麒麟
    • 数据库:SQL Server/MySQL/Oracle + 达梦/人大金仓
  3. 安全与部署
    • 信创国产化环境适配
    • 私有化部署(内网环境)
    • 需提供完整源代码(避免授权成本)

二、技术选型与风险评估

1. 开源组件调研
  • WebUploader:已停更,IE8兼容需依赖Flash(信创浏览器支持差),无官方技术支持。
  • Uppy:现代浏览器支持好,但IE8不兼容,无信创适配方案。
  • FineUploader:商业授权成本高,不适合2000+项目部署。

结论:现有开源方案均无法满足信创兼容+源代码授权+高稳定性需求,需自研或采购商业产品源代码。

2. 自研技术方案

基于公司研发能力,建议采用以下架构:

  • 前端:Vue3 + 自定义分片上传组件(兼容IE8需降级到jQuery+Flash)
  • 后端:JSP/Servlet + 分片合并服务
  • 数据库:通用JDBC适配层(支持多数据库)
  • 加密:国密SM4(可选AES双协议)

三、核心代码实现

1. 前端实现(Vue3 + 兼容IE8方案)
// src/components/FileUploader.vue (主组件)



import { ref } from 'vue';
import SM4 from 'sm4.js'; // 国密加密库

export default {
  setup() {
    const files = ref([]);
    const chunkSize = 10 * 1024 * 1024; // 10MB分片

    // 处理文件夹上传(递归生成文件树)
    const handleFileChange = (e) => {
      const items = e.target.files;
      const fileTree = buildFileTree(items);
      files.value = flattenFileTree(fileTree);
    };

    // 构建文件夹层级结构
    const buildFileTree = (items) => {
      const tree = {};
      Array.from(items).forEach(file => {
        const path = file.webkitRelativePath.split('/');
        let current = tree;
        path.forEach((part, i) => {
          if (i === path.length - 1) {
            current[part] = { file, path: file.webkitRelativePath };
          } else {
            current[part] = current[part] || {};
            current = current[part];
          }
        });
      });
      return tree;
    };

    // 上传逻辑(断点续传核心)
    const startUpload = async () => {
      for (const fileObj of files.value) {
        const { file, path } = fileObj;
        const fileId = await initUpload(file.name, file.size, path);
        
        // 检查已上传分片(从LocalStorage或服务端)
        const uploadedChunks = await checkUploadedChunks(fileId);
        
        for (let start = 0; start < file.size; start += chunkSize) {
          if (uploadedChunks.includes(start / chunkSize)) continue;
          
          const chunk = file.slice(start, start + chunkSize);
          const encryptedChunk = SM4.encrypt(chunk, 'secret-key');
          await uploadChunk(fileId, start, encryptedChunk);
          
          // 保存进度到LocalStorage
          localStorage.setItem(`upload_${fileId}_progress`, start);
        }
        
        await mergeChunks(fileId);
      }
    };

    return { handleFileChange, startUpload };
  }
};

2. 后端实现(JSP/Servlet 分片处理)
// UploadServlet.java (分片上传接口)
@WebServlet("/api/upload")
@MultipartConfig
public class UploadServlet extends HttpServlet {
    private static final String TEMP_DIR = "/data/upload_temp/";

    protected void doPost(HttpServletRequest req, HttpServletResponse resp) {
        String action = req.getParameter("action");
        
        if ("init".equals(action)) {
            // 初始化上传任务(生成唯一ID)
            String fileId = UUID.randomUUID().toString();
            String fileName = req.getParameter("name");
            long fileSize = Long.parseLong(req.getParameter("size"));
            String filePath = req.getParameter("path");
            
            // 保存元数据到数据库(适配多数据库)
            saveToDatabase(fileId, fileName, fileSize, filePath);
            
            resp.getWriter().write(fileId);
        } 
        else if ("chunk".equals(action)) {
            // 处理分片上传
            String fileId = req.getParameter("fileId");
            long chunkIndex = Long.parseLong(req.getParameter("chunkIndex"));
            Part filePart = req.getPart("chunk");
            
            // 解密分片(示例:SM4)
            String encryptedData = IOUtils.toString(filePart.getInputStream(), StandardCharsets.UTF_8);
            byte[] decryptedData = SM4Util.decrypt(encryptedData, "secret-key");
            
            // 保存分片
            Files.write(Paths.get(TEMP_DIR + fileId + "_" + chunkIndex), decryptedData);
            
            // 记录分片状态(Redis或数据库)
            markChunkUploaded(fileId, chunkIndex);
        }
    }
}
3. 断点续传实现(LocalStorage + 服务端校验)
// 前端断点续传逻辑
const checkUploadedChunks = async (fileId) => {
  // 1. 从LocalStorage读取本地进度
  const localProgress = localStorage.getItem(`upload_${fileId}_progress`);
  if (localProgress) {
    return [parseInt(localProgress) / chunkSize];
  }
  
  // 2. 服务端二次校验(避免多设备冲突)
  const response = await fetch(`/api/upload?action=check&fileId=${fileId}`);
  return await response.json();
};

四、信创环境适配方案

  1. 浏览器兼容

    • IE8:使用jQuery + Flash上传组件
    • 信创浏览器:测试龙芯/红莲花浏览器的Flash/HTML5支持
  2. 操作系统适配

    • 统信UOS:提供DEB安装包
    • 中标麒麟:适配RPM依赖管理
  3. 数据库适配

    // 通用JDBC工具类(适配多数据库)
    public class DBUtil {
        private static DataSource dataSource;
        
        static {
            String dbType = System.getProperty("db.type");
            if ("dm".equals(dbType)) {
                // 达梦数据库配置
                dataSource = new DmDataSource();
            } else if ("kingbase".equals(dbType)) {
                // 人大金仓配置
                dataSource = new KingbaseDataSource();
            } else {
                // MySQL/Oracle等
                dataSource = new HikariDataSource();
            }
        }
    }
    

五、风险与解决方案

  1. Flash在信创浏览器的支持问题
    • 方案:提供降级HTML5模式,测试红莲花浏览器的兼容性
  2. 50G文件内存溢出
    • 方案:采用流式加密(分段处理)
  3. 长期技术支持
    • 方案:采购商业产品源代码(如Plupload企业版)或组建内部团队维护

六、下一步计划

  1. 完成文件夹层级结构的完整测试
  2. 搭建统信UOS + 达梦数据库的测试环境
  3. 编写《信创环境部署指南》

(签名:湖南长沙某软件公司 技术经理 李工)

备注:建议优先采购成熟的商业产品源代码(如支持国密的Plupload),避免自研周期过长。如需进一步讨论,请联系技术部邮箱:tech@company.com。

导入项目

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

工程

image

NOSQL

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

创建数据表

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

修改数据库连接信息

image

访问页面进行测试

image

文件存储路径

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

效果预览

文件上传

文件上传

文件刷新续传

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

文件夹上传

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

下载示例

点击下载完整示例

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值