# helper **Repository Path**: luminion/helper ## Basic Information - **Project Name**: helper - **Description**: 封装api的一些常用方法, 提供常用静态工具集, 简化加速开发 - **Primary Language**: Unknown - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: https://github.com/luminion/helper - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2024-12-24 - **Last Updated**: 2026-07-18 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Helper Java Utilities [![Maven Central](https://img.shields.io/maven-central/v/io.github.luminion/helper)](https://central.sonatype.com/artifact/io.github.luminion/helper) [![License](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](LICENSE) [![GitHub stars](https://img.shields.io/github/stars/luminion/helper?style=social)](https://github.com/luminion/helper) Helper 是一组兼容 Java 8 的常用工具类。 项目围绕集合、反射、JSON、HTTP、文件与分片上传、MinIO、FFmpeg、Excel、Spring 上下文和中国地理坐标等常见场景提供轻量能力。 ## 功能特性 - 提供树结构构建、枚举映射、Bean 转换和 Lambda 属性解析 - 提供 Jackson JSON 序列化、反序列化和对象更新封装 - 提供链式 HTTP 请求,支持 query、form、JSON body 与响应流 - 提供本地文件复制、删除和安全的文件分片上传、合并能力 - 提供 MinIO / S3 对象上传、下载、预签名、复制和分片合并能力 - 提供 FFmpeg 播放、转码、裁剪、截图及音视频合并能力 - 提供 EasyExcel / FastExcel 常用类型 converter - 提供 Spring 上下文、动态 Bean 和事件访问能力 - 提供中国境内 WGS-84、GCJ-02、BD-09 坐标转换和范围判断 --- ## 总览 Helper 的基础模块可以直接使用;Spring、Jackson、MinIO、EasyExcel 和 FastExcel 均为 Maven 可选依赖,只有调用对应模块时才需要在业务项目中引入。 | 模块 | 主要能力 | 额外运行时依赖 | | --- | --- | --- | | Collection / Reflect | 树、枚举、Bean、Lambda | 无 | | File / Upload | 文件操作、本地分片上传 | 无 | | HTTP | JDK `HttpURLConnection` 链式封装 | 无 | | Geo / Bit | 坐标、距离、位运算 | 无 | | JSON | Jackson 读写与类型转换 | Jackson Databind、JSR-310 | | Spring | 上下文、Bean、事件 | Spring Context 5.x | | MinIO | S3 对象与分片操作 | MinIO Java SDK 8.x | | Excel | 扩展 converter | EasyExcel 4.x 或 FastExcel 1.x | | FFmpeg | 外部媒体命令封装 | 本机 FFmpeg / FFplay / FFprobe | 资源所有权总原则:明确执行上传的方法会关闭传入的 `InputStream`;MinIO `download` 不关闭调用方提供的 `OutputStream`;`responseStream()` 必须由调用方关闭。 ## Maven 依赖 最新版本 [![Maven Central](https://img.shields.io/maven-central/v/io.github.luminion/helper)](https://central.sonatype.com/artifact/io.github.luminion/helper) ```xml io.github.luminion helper 1.3.0 ``` 项目本身兼容 Java 8。各集成模块的三方依赖在 Helper 中标记为 `optional`,业务项目需按实际使用的模块自行引入。 --- ## 各功能细览 ### 1. 集合与反射 `TreeHelper` 用函数描述节点关系,不要求实体实现固定接口;构树后所有节点的 children 都是可变列表。 ```java TreeHelper tree = TreeHelper.of( Node::getId, Node::getParentId, Node::setChildren ); List roots = tree.treeRootAuto(nodes); List descendants = tree.findAllChildrenById(nodes, parentId); ``` `EnumHelper` 按枚举声明顺序维护 key 映射: ```java EnumHelper statuses = EnumHelper.of(Status.class, Status::getCode); Status status = statuses.requireEnum(1); List all = statuses.enums(); Map labels = statuses.resolveMap(Status::getLabel); ``` Bean 与 Lambda 示例: ```java Target target = BeanHelper.copyProperties(source, Target.class); Target difference = BeanHelper.toDifference(source, target); String property = LambdaHelper.resolvePropertyName(User::getName); ``` 说明: - `BeanHelper.toDifference` 逐属性比较,不依赖业务对象的 `equals` 实现 - `LambdaHelper.resolveSerializedLambda` 始终解析当前方法引用,不缓存捕获对象 - 空集合结果使用可变容器,便于调用方继续补充内容 ### 2. JSON 额外依赖: ```xml com.fasterxml.jackson.core jackson-databind com.fasterxml.jackson.datatype jackson-datatype-jsr310 ``` 使用示例: ```java ObjectMapperHelper json = ObjectMapperHelper.of(); String value = json.toJson(source); Demo parsed = json.parseObject(value, Demo.class); List list = json.parseArray(value, Demo.class); ``` 默认配置: - `long`、`double`、`BigInteger` 和 `BigDecimal` 序列化为字符串 - 日期时间格式为 `yyyy-MM-dd HH:mm:ss` - 可通过 `ObjectMapperHelper.objectMapper(customMapper)` 替换全局实例 ### 3. HTTP ```java String response = HttpHelper.post("https://example.com/api") .header("Authorization", "Bearer token") .queryParam("page", "1") .bodyParam("{\"name\":\"demo\"}") .responseString(); ``` 说明: - `bodyParam` 默认使用 `application/json` - `formParam` 默认使用 `application/x-www-form-urlencoded` - 调用方显式设置的 `Content-Type` 优先 - GET 和 HEAD 不接受请求体 - query 参数会正确追加到原 URL 的 query 中,不会落入 fragment - `responseStream()` 返回的流必须关闭,关闭时会断开底层连接 - 调试日志和非 2xx 日志会原样记录 URL 与请求体,不做脱敏;不得传入不允许写入日志的敏感信息,或应关闭对应日志级别 ### 4. 文件与本地分片上传 文件复制: ```java FileHelper.copyFile(sourceFile, targetFile); FileHelper.copyDir(sourceDir, targetDir); ``` 同文件复制、将目录复制到自身或子目录会直接拒绝。单文件复制先写临时文件,完成后再替换目标。 分片上传: ```java FileUploadHelper upload = new FileUploadHelper("D:/uploads"); upload.uploadChunk(inputStream, fileMd5, "0"); boolean completed = upload.mergeFile(fileMd5, "zip", chunkCount, chunkSize); ``` 说明: - 上传目录及其上级目录不能包含符号链接或目录联接 - `fileMD5` 只接受字母、数字、下划线和短横线 - 分片索引必须是非负整数,扩展名不得包含路径字符 - `uploadChunk` 会关闭传入的输入流 - 推荐使用带 `chunkSize` 的 `mergeFile` 重载校验分片完整性 - 同一 `fileMD5` 的上传、校验、合并和取消在单 JVM 内串行执行 - 多实例部署仍需由业务侧提供分布式协调 ### 5. MinIO / S3 额外依赖: ```xml io.minio minio ``` 创建实例: ```java MinioHelper minio = MinioHelper.builder() .client(minioClient) .bucket("assets") .urlResolver((bucket, objectPath) -> "https://cdn.example.com/" + objectPath) .build(); String url = minio.upload("images/demo.png", inputStream); ``` 分片上传: ```java MinioHelper.ChunkUploadSession session = minio.initChunkUpload("video/demo", "mp4"); minio.uploadChunk(session, 0, firstChunk); minio.uploadChunk(session, 1, secondChunk); String url = minio.completeChunkUpload(session, 2); ``` 说明: - Builder 必须接收已有 `MinioClient` 和 URL 解析函数;默认 bucket 可选 - 上传方法关闭传入的输入流,`download` 保持调用方输出流打开 - 未知长度上传使用 10 MiB 分片;S3 compose 除最后一片外通常要求每片至少 5 MiB - 优先使用 `completeChunkUpload(session, expectedChunkCount)` 明确总片数 - 不带总片数的完成重载已废弃,因为无法判断尾部分片是否缺失 - 合并对象按最终文件名设置 `Content-Type` - `urlResolver` 接收的对象路径已经逐段 URL 编码,不要再次整体编码 - 预签名有效期最短 1 秒、最长 7 天;对象名不允许以 `/` 结尾 ### 6. FFmpeg 运行前需安装 FFmpeg,并提供三个可执行文件路径: ```java FfmpegHelper ffmpeg = FfmpegHelper.of( ffplayPath, ffmpegPath, ffprobePath, tempDir ); ffmpeg.videoFormatConversion(source, target); ``` 说明: - 命令启动失败、输出读取失败、超时或非零退出码会抛出 `IllegalStateException` - 单文件输出先写入目标同目录临时目录,成功且产物非空后再替换目标 - 转换失败时保留旧目标文件,并清理临时产物 - `mergeVideos` 使用 UTF-8 ffconcat 清单并处理特殊字符路径 - `videoAllScreenshot` 会生成多个文件,不适用单文件原子发布;建议使用独立输出目录 - 输入路径和外部命令参数应来自可信来源 ### 7. Excel 按实际使用的库引入依赖: ```xml com.alibaba easyexcel ``` ```xml cn.idev.excel fastexcel ``` 注册 converter: ```java EasyExcelHelper.registerExtraConverters(); // 或 FastExcelHelper.registerExtraConverters(); ``` 说明: - 默认日期格式为 `yyyy-MM-dd` - 默认时间格式为 `HH:mm:ss` - 默认日期时间格式为 `yyyy-MM-dd HH:mm:ss` - 默认时区为 `GMT+8` - Boolean 转换器将 `TRUE`、`TURE`、`1`、`Y`、`YES`、`是`(忽略大小写和首尾空格)识别为 `true` - 其他非空 Boolean 文本恒为 `false`,空白单元格返回 `null` ### 8. Spring 上下文 额外依赖: ```xml org.springframework spring-context ``` 使用示例: ```java UserService service = SpringContextHelper.getBean(UserService.class); OptionalService optional = SpringContextHelper.getBeanIfPresent(OptionalService.class); SpringContextHelper.registerBean("runtimeBean", runtimeBean); SpringContextHelper.publishEvent(new DemoEvent(source)); ``` 说明: - `getBeanIfPresent` 只在 Bean 不存在时返回 `null` - Bean 创建或依赖注入失败会继续抛出,不会伪装成“Bean 不存在” - Spring 上下文关闭后会清理静态引用 - 动态 Bean 操作要求应用上下文已完成初始化且尚未关闭 ### 9. 地理坐标 ```java GeoHelper wgs84 = GeoHelper.ofWGS84(116.397, 39.908); GeoHelper gcj02 = wgs84.toGCJ02(); double distance = wgs84.getDistanceMeters(otherPoint); boolean inside = wgs84.isInPolygon(boundaryPoints); ``` 说明: - 支持 WGS-84、GCJ-02 与 BD-09 转换 - 支持距离、圆形、矩形和多边形范围判断 - 范围判断只面向中国境内坐标,不支持跨越 `+180/-180` 反经线的区域 - GCJ-02 / BD-09 逆转换存在约 1~2 米误差 --- ## 异常与资源约定 - 参数错误通常抛出 `IllegalArgumentException` - 外部系统、文件或进程调用失败通常包装为 `IllegalStateException` - 上传方法会关闭传入的 `InputStream` - MinIO 下载不关闭调用方提供的 `OutputStream` - HTTP 响应流必须由调用方关闭 - HTTP 原文日志按当前设计不脱敏,生产环境需控制日志级别和输入内容 - 文件上传并发锁只在单 JVM 内有效 --- ## 故障排查 FAQ ### Q1:引入依赖后某个 Helper 报 `ClassNotFoundException`? Helper 的集成依赖是可选依赖。使用 JSON、Spring、MinIO、EasyExcel 或 FastExcel 时,需要在业务项目中显式引入对应三方库。 ### Q2:为什么 GET / HEAD 设置 body 会失败? JDK `HttpURLConnection` 可能在写 GET 请求体时把请求隐式改成 POST。为避免方法语义被静默改变,Helper 明确拒绝 GET 和 HEAD 请求体;请改用 query 参数或合适的 HTTP 方法。 ### Q3:为什么本地文件分片上传在多实例部署中仍需加锁? `FileUploadHelper` 的锁是当前 JVM 内锁,只能协调单进程内的上传、校验、合并和取消。多个应用实例共享同一磁盘时,需要在业务侧增加分布式锁或按实例隔离上传目录。 ### Q4:为什么 MinIO 分片完成建议传 `expectedChunkCount`? 仅根据已经列出的连续分片,无法判断最后是否还缺少一片。带总片数的重载可以同时校验数量和索引连续性,避免发布尾部缺失的对象。 ### Q5:FFmpeg 失败后旧目标文件还在吗? 单文件输出方法会先生成临时产物,成功且产物非空后才替换目标。启动失败、超时、非零退出或无有效产物时,旧目标文件保持不变。 ### Q6:Excel 中哪些值会转换为 `true`? 只有 `TRUE`、`TURE`、`1`、`Y`、`YES`、`是` 会转换为 `true`;比较忽略大小写和首尾空格。其他非空值统一转换为 `false`。 ### Q7:为什么地理范围判断不支持跨反经线区域? 该工具面向中国境内坐标,矩形和多边形按普通经纬度平面边界计算,不处理 `+180/-180` 经度解缠。全球或跨反经线场景应使用球面几何库。 --- 完整版本变更见 [CHANGELOG.md](CHANGELOG.md)。