ByteSource 不自动关闭文件描述符,仅提供 openStream() 接口;read() 和 copyTo() 内部自动关源流,openStream() 必须配合 try-with-resources 手动管理。

ByteSource 是什么,它真能自动关文件描述符吗
不能。ByteSource 本身不自动关闭底层资源——它只提供统一的 openStream() 接口,但返回的 InputStream 仍需手动关闭。Guava 的设计哲学是「不隐藏资源生命周期」,所以你看到的 ByteSource.wrap(...) 或 Files.asByteSource(...) 都不会在读取后自动调用 close()。
真正能帮你管理闭包的,是配合 try-with-resources 或显式调用 asCharSource(...).read() 这类「一次性消费」方法——它们内部会确保流被关闭。
-
ByteSource.read():把全部内容读进 byte[],自动关流 -
ByteSource.copyTo(OutputStream):流式复制,自动关源流(但不关目标流) -
ByteSource.openStream():必须自己 try-with-resources,否则 fd 泄漏
多来源文件怎么统一成 ByteSource
Guava 提供了开箱即用的工厂方法,覆盖常见来源,关键是选对构造方式,避免隐式打开 fd:
- 本地文件:
Files.asByteSource(new File("/tmp/a.txt"))—— 此时还没打开 fd,安全 - Classpath 资源:
Resources.asByteSource(Resources.getResource("config.json")) - 字节数组:
ByteSource.wrap(new byte[]{0x1, 0x2})—— 零 fd 开销 - InputStream(慎用):
ByteSource.wrap(inputStream)—— 注意该流是否已打开、能否重复读
别用 new ByteSource() { ... } 手写匿名子类,除非你明确控制 openStream() 中的打开/关闭逻辑;否则容易漏关或重复关。
流式处理多个 ByteSource 时 fd 泄漏的典型场景
最常踩的坑是链式调用中忘记关流,尤其是嵌套 openStream():
ByteSource src = Files.asByteSource(file); // ❌ 错误:没关流,fd 泄漏 InputStream is = src.openStream(); is.readAllBytes(); // 但 is 没 close
更隐蔽的是并行处理多个 ByteSource 时未做异常保护:
- 用
stream().map(src -> src.openStream())生成一堆未关闭流 - 在
forEach中读取但没try,异常时跳过close() -
ByteSource.concat(...)返回新ByteSource,但 concat 后的openStream()仍需手动关
正确做法是每个 openStream() 必须配对 try-with-resources,哪怕只是 peek 一下长度:src.size() 是安全的(不打开流),但 src.hash(...) 会打开并自动关。
一个安全的多源流式处理模板
假设你要从磁盘、classpath、网络响应(已转为 byte[])三处读取并拼接处理:
ByteSource a = Files.asByteSource(new File("/etc/hosts"));
ByteSource b = Resources.asByteSource(Resources.getResource("default.conf"));
ByteSource c = ByteSource.wrap(responseBody);
// ✅ 安全:concat 不打开流,copyTo 内部关源
ByteSource all = ByteSource.concat(a, b, c);
all.copyTo(System.out); // 自动关所有源流
// ✅ 或逐个处理(带异常防护)
for (ByteSource src : Arrays.asList(a, b, c)) {
try (InputStream is = src.openStream()) {
processStream(is);
}
}
注意 ByteSource.concat() 返回的 ByteSource 在 copyTo() 或 read() 时才会依次打开各源,并保证每个都关闭;但它不支持随机 seek,也不适合超大文件拼接(会串行打开)。
真正的难点不在拼,而在「谁负责关」——只要记住:Guava 从不替你做资源清理,它只让关的动作更容易被封装和复用。










