学习内容基于MinIO搭建对象存储承载大文件上传实现大文件分片上传与断点续传优化后 1GB 文件上传耗时由 15s 降至 3s。一、MD5算法1、概念MD5是一种哈希算法也叫散列算法核心作用是把任意长度的输入数据如字符串、文件、二进制流通过特定计算规则生成一个固定 128 位16 字节的哈希值通常以 32 位十六进制字符串形式展示。我们可以用一个通俗的比喻理解就算是把一篇 1 万字的文章任意长度输入交给 MD5 “机器”它也会输出一个固定长度的 “指纹”32 位十六进制字符串哪怕只改文章里的一个标点输出的 “指纹” 也会完全不同但你无法通过这个 “指纹” 反推出原来的文章内容。2、核心特性固定长度输出无论输入是 1KB 的文件还是 1GB 的视频MD5 结果都是 32 位十六进制字符串如e10adc3949ba59abbe56e057f20f883e雪崩效应输入的微小变化哪怕 1 个字符会导致输出的哈希值完全不同不可逆性无法从 MD5 哈希值反推原始数据这也是它早期用于密码加密的核心原因易计算对任意输入能快速算出对应的 MD5 值。3、适用场景1文件完整性校验在下载软件或者安装包时官网一般会提供一个该文件的MD5值我们下载后计算文件MD5值如果一致则表示文件未被篡改无传输错误如果不一致那么文件可能被篡改或者下载错误。2数据唯一标识在MinIO/文件存储场景中常用MD5算法给文件命名避免文件名重复如果用户上传的是同名图片可以用MD5作为唯一Key存储可以快速判断文件是否存在计算新文件的MD5如果存储中已有则无需重复存储二、传统文件存储方式与改进以前的做法是专门开一台集中式文件服务器里边存文件用Tomcat对外访问现在的做法是使用MinIO存储对象数据库存路径是分布式对象存储1、MinIO分布式对象存储的概念MinIO是分布式对象存储系统专门用于存储海量的非结构化的数据图片视频文档日志文件等。传统文件服务器是类似于单间仓库仓库坏了就都没了但是MinIO的关键在于分布式模式这种是多间联网的仓库集群文件会被拆分到多间仓库即使一间坏了数据也可以从其他仓库恢复且多间仓库可同时对外提供服务效率高。2、两者区别与比较维度传统文件服务器Tomcat 集中式服务器MinIO 分布式对象存储存储架构集中式所有文件存在单台服务器的本地磁盘分布式数据分散存储在多台服务器节点支持集群部署高可用性极低服务器宕机 / 磁盘损坏所有文件不可用 / 丢失极高多副本备份可配置 1-16 副本部分节点故障不影响数据访问支持自动恢复扩展性差扩容需更换更大磁盘 / 服务器停机维护好线性扩容直接添加新节点即可无需停机存储容量随节点数增长并发性能低单台服务器的 CPU / 网卡 / 磁盘 IO 是瓶颈高并发下易卡顿高集群节点并行处理请求分摊压力支持海量并发读写数据管理依赖操作系统文件系统如 NTFS/Ext4手动管理文件夹 / 权限易出现文件重名、路径混乱基于对象存储模型自带权限控制、版本管理、元数据管理可通过 API 统一管控访问方式主要通过 Tomcat 提供 HTTP 访问需自己开发文件上传 / 下载接口适配性差兼容 S3 协议提供标准化 API/SDK/ 工具支持 HTTP/HTTPS可直接对接各种业务系统Java/PHP/Python 等运维成本高需手动备份、监控单台服务器故障恢复慢低自带监控、自动容错故障节点替换后自动同步数据运维更轻量化适用场景小型系统、低并发、少量文件存储如内部管理系统中大型系统、高并发、海量非结构化数据如电商、短视频、日志存储三、分片上传时用什么作为文件的唯一标识困惑为什么使用的是MD5算法而不是原来用于给文件名称命名时生成的uuid算法其区别对比如下1、MD5与UUID的核心特性对比维度MD5UUID如 UUID4生成依据基于文件内容内容相同则值相同基于时间 / 随机数和内容无关核心特性内容唯一 → MD5 唯一全局唯一但和内容无关长度32 位十六进制字符串短小36 位字符串含-更长可预测性相同文件可预测 MD5 值完全随机不可预测核心用途内容校验、文件指纹、去重分布式系统唯一 ID、主键2、为什么分片上传场景下不能用UUID代替MD51. 核心问题无法实现文件去重假设用户上传同一个文件比如test.zip两次用 MD5两次生成的 MD5 完全相同后端能识别出 “这是同一个文件”直接返回已上传结果无需重复存储用 UUID两次生成的 UUID 完全不同后端会认为是 “两个不同的文件”最终在 MinIO 里存储两份一模一样的内容造成严重的存储浪费。UUID 最大的缺陷 —— 它只保证 “ID 唯一”但不关心 “内容是否相同”而文件上传场景的核心诉求之一就是「内容相同即视为同一个文件」。2. 断点续传逻辑会失效分片上传的断点续传依赖「同一个文件的所有分片能关联到同一个任务 ID」用 MD5用户中断上传后重新上传同一个文件时MD5 不变后端能通过 MD5 查到之前的上传进度已传分片列表只传未完成的分片用 UUID用户重新上传时生成的 UUID 是新的后端会认为是 “新的上传任务”只能从头开始传所有分片断点续传彻底失效。3. 额外的存储 / 检索成本UUID 比 MD5 长36 位 vs 32 位作为 Redis 键、MinIO 存储路径时占用更多内存Redis 键越长内存消耗越大路径层级设计更繁琐UUID 无规律无法像 MD5 那样拆分为xx/xx/xxxx目录结构分散文件易造成单目录文件过多不利于人工排查MD5 可通过文件内容反查UUID 纯随机无法关联到文件内容。四、Spring注解驱动的IoC容器机制Spring 启动时会扫描特定注解把标注的类 / 方法生成的对象 “收编” 到自己的容器里后续我们需要用时直接拿不用自己 new。注解作用Configuration告诉 Spring这是一个配置类里面会定义需要被 Spring 管理的对象BeanValue从配置文件application.yml/properties里读取配置值赋值给当前字段Bean告诉 Spring执行这个方法把方法返回的对象 “注册” 到 Spring 容器中1、Spring自动管理这个类的完整流程步骤 1扫描注解找到你的 MinioConfigSpring 启动后会先执行 “包扫描”默认扫描启动类所在包及其子包当扫描到MinioConfig上的Configuration注解时会标记这个类是配置类我要重点处理它里面的内容。步骤 2初始化 MinioConfig 类对象Spring 会先创建MinioConfig这个类的实例相当于你自己写new MinioConfig()然后读取application.yml里的minio.endpointminio.accessKey等配置通过Value注解把这些配置值赋值给类里的endpointaccessKey等字段。步骤 3执行 Bean 方法注册对象到容器Spring 会遍历MinioConfig里所有带Bean的方法逐个执行执行minioClient()方法调用MinioClient.builder()构建客户端对象把这个MinioClient对象 “存” 到 Spring 容器中默认以方法名minioClient作为这个对象的 “标识”执行minioPublicUrl()方法把publicUrl的值返回同样存到 Spring 容器中标识是minioPublicUrl。步骤 4后续使用自动注入当你在 Service 里写Service public class MinioService { // 从 Spring 容器中取出标识为 minioClient 的对象 Autowired private MinioClient minioClient; // 取出标识为 minioPublicUrl 的字符串 Autowired private String minioPublicUrl; }Spring 会自动把容器里的MinioClient和String对象赋值给这两个字段我们不用自己 new这就是 “自动管理” 的核心体现。
Day1学习笔记 --AIAgent 项目 --文件上传与解析(一)
学习内容基于MinIO搭建对象存储承载大文件上传实现大文件分片上传与断点续传优化后 1GB 文件上传耗时由 15s 降至 3s。一、MD5算法1、概念MD5是一种哈希算法也叫散列算法核心作用是把任意长度的输入数据如字符串、文件、二进制流通过特定计算规则生成一个固定 128 位16 字节的哈希值通常以 32 位十六进制字符串形式展示。我们可以用一个通俗的比喻理解就算是把一篇 1 万字的文章任意长度输入交给 MD5 “机器”它也会输出一个固定长度的 “指纹”32 位十六进制字符串哪怕只改文章里的一个标点输出的 “指纹” 也会完全不同但你无法通过这个 “指纹” 反推出原来的文章内容。2、核心特性固定长度输出无论输入是 1KB 的文件还是 1GB 的视频MD5 结果都是 32 位十六进制字符串如e10adc3949ba59abbe56e057f20f883e雪崩效应输入的微小变化哪怕 1 个字符会导致输出的哈希值完全不同不可逆性无法从 MD5 哈希值反推原始数据这也是它早期用于密码加密的核心原因易计算对任意输入能快速算出对应的 MD5 值。3、适用场景1文件完整性校验在下载软件或者安装包时官网一般会提供一个该文件的MD5值我们下载后计算文件MD5值如果一致则表示文件未被篡改无传输错误如果不一致那么文件可能被篡改或者下载错误。2数据唯一标识在MinIO/文件存储场景中常用MD5算法给文件命名避免文件名重复如果用户上传的是同名图片可以用MD5作为唯一Key存储可以快速判断文件是否存在计算新文件的MD5如果存储中已有则无需重复存储二、传统文件存储方式与改进以前的做法是专门开一台集中式文件服务器里边存文件用Tomcat对外访问现在的做法是使用MinIO存储对象数据库存路径是分布式对象存储1、MinIO分布式对象存储的概念MinIO是分布式对象存储系统专门用于存储海量的非结构化的数据图片视频文档日志文件等。传统文件服务器是类似于单间仓库仓库坏了就都没了但是MinIO的关键在于分布式模式这种是多间联网的仓库集群文件会被拆分到多间仓库即使一间坏了数据也可以从其他仓库恢复且多间仓库可同时对外提供服务效率高。2、两者区别与比较维度传统文件服务器Tomcat 集中式服务器MinIO 分布式对象存储存储架构集中式所有文件存在单台服务器的本地磁盘分布式数据分散存储在多台服务器节点支持集群部署高可用性极低服务器宕机 / 磁盘损坏所有文件不可用 / 丢失极高多副本备份可配置 1-16 副本部分节点故障不影响数据访问支持自动恢复扩展性差扩容需更换更大磁盘 / 服务器停机维护好线性扩容直接添加新节点即可无需停机存储容量随节点数增长并发性能低单台服务器的 CPU / 网卡 / 磁盘 IO 是瓶颈高并发下易卡顿高集群节点并行处理请求分摊压力支持海量并发读写数据管理依赖操作系统文件系统如 NTFS/Ext4手动管理文件夹 / 权限易出现文件重名、路径混乱基于对象存储模型自带权限控制、版本管理、元数据管理可通过 API 统一管控访问方式主要通过 Tomcat 提供 HTTP 访问需自己开发文件上传 / 下载接口适配性差兼容 S3 协议提供标准化 API/SDK/ 工具支持 HTTP/HTTPS可直接对接各种业务系统Java/PHP/Python 等运维成本高需手动备份、监控单台服务器故障恢复慢低自带监控、自动容错故障节点替换后自动同步数据运维更轻量化适用场景小型系统、低并发、少量文件存储如内部管理系统中大型系统、高并发、海量非结构化数据如电商、短视频、日志存储三、分片上传时用什么作为文件的唯一标识困惑为什么使用的是MD5算法而不是原来用于给文件名称命名时生成的uuid算法其区别对比如下1、MD5与UUID的核心特性对比维度MD5UUID如 UUID4生成依据基于文件内容内容相同则值相同基于时间 / 随机数和内容无关核心特性内容唯一 → MD5 唯一全局唯一但和内容无关长度32 位十六进制字符串短小36 位字符串含-更长可预测性相同文件可预测 MD5 值完全随机不可预测核心用途内容校验、文件指纹、去重分布式系统唯一 ID、主键2、为什么分片上传场景下不能用UUID代替MD51. 核心问题无法实现文件去重假设用户上传同一个文件比如test.zip两次用 MD5两次生成的 MD5 完全相同后端能识别出 “这是同一个文件”直接返回已上传结果无需重复存储用 UUID两次生成的 UUID 完全不同后端会认为是 “两个不同的文件”最终在 MinIO 里存储两份一模一样的内容造成严重的存储浪费。UUID 最大的缺陷 —— 它只保证 “ID 唯一”但不关心 “内容是否相同”而文件上传场景的核心诉求之一就是「内容相同即视为同一个文件」。2. 断点续传逻辑会失效分片上传的断点续传依赖「同一个文件的所有分片能关联到同一个任务 ID」用 MD5用户中断上传后重新上传同一个文件时MD5 不变后端能通过 MD5 查到之前的上传进度已传分片列表只传未完成的分片用 UUID用户重新上传时生成的 UUID 是新的后端会认为是 “新的上传任务”只能从头开始传所有分片断点续传彻底失效。3. 额外的存储 / 检索成本UUID 比 MD5 长36 位 vs 32 位作为 Redis 键、MinIO 存储路径时占用更多内存Redis 键越长内存消耗越大路径层级设计更繁琐UUID 无规律无法像 MD5 那样拆分为xx/xx/xxxx目录结构分散文件易造成单目录文件过多不利于人工排查MD5 可通过文件内容反查UUID 纯随机无法关联到文件内容。四、Spring注解驱动的IoC容器机制Spring 启动时会扫描特定注解把标注的类 / 方法生成的对象 “收编” 到自己的容器里后续我们需要用时直接拿不用自己 new。注解作用Configuration告诉 Spring这是一个配置类里面会定义需要被 Spring 管理的对象BeanValue从配置文件application.yml/properties里读取配置值赋值给当前字段Bean告诉 Spring执行这个方法把方法返回的对象 “注册” 到 Spring 容器中1、Spring自动管理这个类的完整流程步骤 1扫描注解找到你的 MinioConfigSpring 启动后会先执行 “包扫描”默认扫描启动类所在包及其子包当扫描到MinioConfig上的Configuration注解时会标记这个类是配置类我要重点处理它里面的内容。步骤 2初始化 MinioConfig 类对象Spring 会先创建MinioConfig这个类的实例相当于你自己写new MinioConfig()然后读取application.yml里的minio.endpointminio.accessKey等配置通过Value注解把这些配置值赋值给类里的endpointaccessKey等字段。步骤 3执行 Bean 方法注册对象到容器Spring 会遍历MinioConfig里所有带Bean的方法逐个执行执行minioClient()方法调用MinioClient.builder()构建客户端对象把这个MinioClient对象 “存” 到 Spring 容器中默认以方法名minioClient作为这个对象的 “标识”执行minioPublicUrl()方法把publicUrl的值返回同样存到 Spring 容器中标识是minioPublicUrl。步骤 4后续使用自动注入当你在 Service 里写Service public class MinioService { // 从 Spring 容器中取出标识为 minioClient 的对象 Autowired private MinioClient minioClient; // 取出标识为 minioPublicUrl 的字符串 Autowired private String minioPublicUrl; }Spring 会自动把容器里的MinioClient和String对象赋值给这两个字段我们不用自己 new这就是 “自动管理” 的核心体现。