ByteSource能自动管理资源闭包,因为它不持有打开的流,每次调用read()、copyTo()或openStream()时才新建InputStream,并用try-with-resources自动关闭;所有高层方法均已内置安全闭环。

ByteSource 为什么能自动管理资源闭包
因为 ByteSource 本身不持有打开的流,它只提供「按需创建新流」的能力。每次调用 read()、copyTo() 或 openStream() 时,才真正打开一个 InputStream,且该流会在操作完成后**自动关闭**(内部用 try-with-resources 封装)。这和直接 new FileInputStream 完全不同——后者必须手动 close,漏掉就泄漏句柄。
关键点在于:只要你不显式调用 openStream() 并自己 hold 住返回的流,就完全不用操心 close。所有高层方法(如 read()、hash()、size())都已内置安全闭环。
支持哪些多来源?怎么创建对应 ByteSource
常见来源有文件、字节数组、URL、临时路径、甚至内存映射。创建方式统一但参数语义不同:
-
Files.asByteSource(new File("a.bin")):底层走 NIOFiles.newInputStream,支持符号链接解析(默认跟随) -
ByteSource.wrap(new byte[]{1,2,3}):纯内存,无 IO,read()直接返回副本,线程安全 -
Resources.asByteSource(SomeClass.class.getResource("/data.bin")):从 classpath 加载,注意路径前缀斜杠 -
MoreFiles.asByteSource(Paths.get("/tmp/x"), StandardOpenOption.READ):比 Files 更细粒度控制 open options,比如加StandardOpenOption.NOFOLLOW_LINKS
⚠️ 注意:Resources.asByteSource 在类路径中找不到资源时抛 NullPointerException,不是 IOException;而 Files.asByteSource 对不存在的文件抛 FileNotFoundException(运行时异常子类)。
流式处理大文件时别踩 size() 和 read() 的坑
这两个方法看似方便,但对大文件极易 OOM 或阻塞:
-
size()对普通文件是Files.size(path),快;但对 URL 或 zip 内部资源,会触发完整下载或解压再统计,不可控 -
read()总是把整个内容加载进内存,哪怕 500MB 文件也会 new 一个 500MB byte[] - 正确做法是用
copyTo(OutputStream)或openStream()+ 自己的缓冲逻辑
例如流式校验 SHA256:
HashCode hash = source.hash(Hashing.sha256()); // 内部用小缓冲逐块读,不占大内存 System.out.println(hash.toString());
又如边读边写到另一个 sink(不落地全量内存):
source.copyTo(MoreFiles.asByteSink(Paths.get("out.bin")));
自定义 ByteSource 时如何保证资源安全释放
如果你继承 ByteSource 实现自己的来源(比如对接 S3 或数据库 BLOB),必须重写 openStream(),且返回的 InputStream 必须是可关闭的,并确保其 close() 方法真正释放远端连接或句柄。
不要在 openStream() 里缓存或复用流实例——ByteSource 的设计假设每次调用都应返回**全新、独立、可关闭**的流。否则多个 read() 调用会互相干扰或提前关闭。
最简验证方式:连续两次调用 source.read(),结果应一致且不抛 IOException(除非源头本身变化)。
复杂点在于异步或网络源的超时与重试策略,这些 Guava 不管,得你在 openStream() 里自行封装,但记得别破坏「每次调用新建流」这个契约。

















