外观
文件与图片
FileBase64Util、FileUtil、ImageCompressorUtil 位于 com.own.constant.util。它们处理文件内容和本地路径,不维护业务文件记录,也不自动接入对象存储;持久化资源管理见资源文件。
| 要完成的任务 | 从哪里开始 |
|---|---|
| 将文件内容转成 Base64,或还原为流 | 文件 Base64 |
| 限制图片宽高、文件大小或转换格式 | 图片压缩 |
| 精确复制文件或保存输入流 | 使用 JDK Files.copy |
| 维护已有 FileUtil 调用 | 阅读目录副作用及下载、写入限制 |
文件 Base64
从本地普通文件读取并编码,再解码为流:
java
import com.own.constant.util.FileBase64Util;
import java.nio.file.Path;
String encoded = FileBase64Util.fileToBase64(Path.of("input/report.pdf"));
if (encoded.isEmpty()) {
throw new IllegalStateException("文件为空或编码失败");
}
try (var input = FileBase64Util.base64ToInputStream(encoded)) {
System.out.println("字节数=" + input.readAllBytes().length);
}代码放在允许抛出 IOException 的方法中,运行前准备输入文件。空文件和失败都可能得到空字符串;需要区分时应先检查文件或使用返回明确异常的读写 API。
| 方法 | 行为与资源归属 |
|---|---|
toBase64(path) | 根据 HTTP/HTTPS scheme 选择 URL 或本地路径 |
pathToBase64(path) / fileToBase64(Path) | 读取普通文件;字符串路径会 trim,失败通常返回空字符串 |
inputStreamToBase64(input) | 将剩余内容编码到内存,不关闭调用方输入流 |
urlToBase64(url[, timeout]) | GET 请求,仅编码 2xx 响应;由工具关闭响应流 |
base64ToInputStream(text) | MIME Base64 解码;空内容或解码失败返回空流 |
base64ToFile(text) | 解码到系统临时文件 base64-*.tmp;空内容或失败返回 null |
网络连接超时固定为 5 秒,请求超时默认 5 秒,可传 Duration;null、零或负数回退默认值。客户端跟随普通重定向,非 2xx、I/O、非法 URL 或中断返回空字符串,中断会恢复线程中断标记。请求超时设置不应当作完整响应体读取的独立限时保证。
解码使用 MIME Decoder,可能忽略非 Base64 字符,不适合严格验证输入。Data URL 只识别 data: 前缀与逗号,不验证媒体类型或 base64 声明。编码结果和解码内容都在内存中,网络读取没有大小上限;调用方控制来源与大小。base64ToFile 成功返回的文件需要调用方删除。
图片压缩
以下示例将已有图片等比例压到最大 1024 × 1024,目标不超过 300 KiB,并写到指定 JPEG 文件:
java
import com.own.constant.ConstantFile;
import com.own.constant.util.ImageCompressorUtil;
import java.io.File;
var options = ImageCompressorUtil.Options.maxDimension(1024, 1024)
.withMaxSizeKB(300)
.withQuality(0.85f)
.withOutputFormat(ConstantFile.Format.JPEG);
File result = ImageCompressorUtil.compress(
new File("input/avatar.png"),
new File("output/avatar.jpg"),
options);
System.out.println(result.length());输入必须是存在且可读的普通文件,并能被当前 ImageIO Reader 识别。输出覆盖指定路径,父目录自动创建;调用方确认目标路径。成功时输出字节数不超过 300 * 1024,尺寸受最大宽高约束。
Options 默认值与调整
Options 是 record,所有 with... 方法返回新对象,不修改原配置。
| 字段 | 默认值 | 调整入口与约束 |
|---|---|---|
maxWidth/maxHeight | 0/0 | withMaxDimension;0 表示不限制,不能负数 |
maxSizeBytes | 0 | withMaxSizeBytes / withMaxSizeKB;0 不限制,不能负数 |
quality | 0.85 | withQuality;应为有限值且满足 0 < quality <= 1 |
outputFormat | null | withOutputFormat;裁剪空白、转小写、移除前导点,jpg 归一为 jpeg |
allowUpscale | false | withAllowUpscale;默认不放大小图 |
scalingMode | BALANCED | withScalingMode;null 回退 BALANCED |
可用 Options.defaults()、maxDimension(width,height) 或 maxSize(bytes) 开始组合。withMaxSizeKB 直接乘 1024,不检查算术溢出;构造器的质量比较也没有主动拒绝 NaN,应用需限制输入。
缩放模式 FAST、BALANCED、QUALITY 分别使用最近邻、双线性、双三次插值。输出格式按“显式配置 → 目标后缀 → 源后缀 → 透明图 PNG / 其他 JPEG”选择;JPEG/BMP 会把透明区合成白色背景。
ConstantFile 列出某个格式不表示当前 ImageIO 有对应编解码器;缺少 Writer 时压缩失败。实现只读取、写出单张 BufferedImage,不保留动画帧或原文件元数据。
大小限制与失败
设置目标大小后,JPEG 会尝试调整质量,仍超限则最多进行 12 轮缩小尺寸;其他格式主要依靠尺寸缩小。最终仍不满足目标时抛异常,不返回超限文件。
compress 将 I/O 与参数异常包装为 BusinessSimpleException,原错误保存在 getError()。在调用前构造 Options 时发生的参数异常则直接抛出,不经过压缩方法包装。
不传目标文件的两个重载 compress(source)、compress(source, options) 会创建系统临时文件,成功后由调用方清理;在创建文件后压缩失败也没有自动清理保证。设置明确 outputFormat 可以避免从文件名推断格式;否则推断过程会调用 FileUtil,触发下述类初始化副作用。
本地目录与路径工具
FileUtil 首次主动使用会在进程工作目录下创建 files/upload、files/backup、files/temp。即使只是调用文件名解析方法,也可能触发这些目录创建和临时文件清理。
这些目录不是按请求或租户隔离的存储空间;不要将仍在使用的文件放入会被自动清理的 temp 目录。只需要自行管理的本地路径时,可使用 JDK Path / Files,无需引入此处的目录约定。
FileUtil 目录与路径方法速查
| 方法 | 当前行为 |
|---|---|
getStaticFileFolder/getUploadFolder/getBackupFolder | 返回工作目录下对应路径 |
getTempFolder() | 返回路径前触发清理;根目录普通文件超过 20 时按创建时间清到 19 个,删除失败可能留存更多 |
createTempFile(prefix, suffix) | 只返回 files/temp/<prefix><suffix> 对应 File,不创建文件、不保证唯一;也会触发清理 |
generateFolder(path) | 不存在时 mkdirs,不检查成功结果 |
deleteFile(file) | 递归删除,null/不存在时忽略,删除失败静默 |
randomName(extension) | 去横线 UUID + 点 + extension;参数应使用不带点的 ConstantFile.Extension |
extractUriExtension(text) | 返回带点小写后缀,会截去其后的查询参数;不完整处理 fragment 或 URL 结构 |
extractFileNameFromUrl/Uri(...[, suffix]) | 默认保留后缀,false 去最后后缀;非法 URI/编码可抛异常 |
getOriginFromUri(text) | 按 http 前缀和字符串位置截取来源,不是严格 URI 验证 |
下载、摘要与写入
FileUtil.write(folder, fileName, InputStream) 的写循环忽略每次实际读取的字节数,可能写入额外字节。需要精确保存输入流时,使用 Files.copy 并自行关闭流。例如,将下列片段放在允许抛出 IOException 的方法中,运行前准备 input/report.pdf:
java
import java.nio.file.Files;
import java.nio.file.Path;
Path target = Path.of("output/report-copy.pdf");
Files.createDirectories(target.getParent());
try (var input = Files.newInputStream(Path.of("input/report.pdf"))) {
long bytes = Files.copy(input, target);
System.out.println("已复制字节数=" + bytes);
}成功时目标内容与输入一致,输出实际复制的字节数。目标已存在时抛异常;确实需要覆盖时再传入 StandardCopyOption.REPLACE_EXISTING。
FileUtil.downloadFile 没有设置超时、不检查 HTTP 状态且默认不跟随重定向,错误响应体也可能保存为文件。新下载流程需要由应用明确处理超时、状态与内容大小,不能以“返回 File”判断下载成功。
已有 FileUtil 下载、摘要与写入调用的行为
| 入口 | 返回与限制 |
|---|---|
getFile(text) | 空白为 null,http 前缀触发下载,其他文本仅构造 File,不检查存在性 |
getFileMd5(String/File) | 返回小写 MD5;I/O 失败为空字符串。字符串 URL 会先下载,临时文件不会自动删除 |
downloadFile(url[, folder[, fileName[, forceUpdateExtension]]]) | 保存到指定或模块临时目录;异常包装为 BusinessSimpleException |
write(fileName, content) / write(fileName, suffix, content) | UTF-8 文本写到模块 temp,前者默认 .txt,注册 deleteOnExit;I/O 失败返回 null |
copyFile(source, target) | 复制字节;目标流先于父目录创建而打开,父目录不存在时会失败 |
downloadFile 显式传 fileName 时,forceUpdateExtension=true(默认)会追加 URL 后缀,例如 a.jpg 加 .png 得到 a.jpg.png;false 时已有点的名称保留,否则仍追加后缀。
FileUtil.write(folder, fileName, InputStream) 会关闭输入流;前述写入限制同样影响文本流,不只影响图片。