You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java获取文件实时大小的问题咨询

Java获取文件实时大小的问题咨询

你好,我来帮你分析下这个问题哈!

首先,你遇到的核心问题是:写文件的后台进程在持续写入,但查询文件大小的API只有等整个文件写完才返回数值,之前一直拿到空/0的结果。咱们先搞清楚原因,再给你具体的解决办法。

为什么当前查询API拿不到实时大小?

你的查询代码本身是没问题的,Files.size()确实能获取文件的实时大小,但有个前提:已经写入的数据必须被刷到磁盘上。

看你的写文件代码,用了BufferedOutputStream做缓冲,而且只有当两次写入间隔超过2秒的时候才会调用flush()。这就导致:

  1. 在两次flush的间隔内,写入的数据都存在内存缓冲区里,根本没写到磁盘,这时候Files.size()读的是磁盘上的文件,自然拿不到这部分数据的大小;
  2. 哪怕你触发了flush,有些操作系统还会把数据存在自己的磁盘缓冲区里,没有真正落地到磁盘,这时候Files.size()也可能拿不到最新值;
  3. 直到整个文件写完,try-with-resources自动关闭流的时候,才会强制把所有缓冲区的数据刷到磁盘,这时候你才能拿到完整的文件大小。

具体解决办法(推荐方案优先)

方案一:维护内存计数器(推荐,实时性+性能兼顾)

这个办法不需要改查询API的核心逻辑,而是在写文件的代码里加一个线程安全的计数器,实时记录已经写入的字节数。查询的时候直接读这个计数器,不用等数据刷到磁盘,既快又准。

步骤如下:

  1. 在你跑后台写文件的类里,加一个线程安全的计数器:
// AtomicLong保证多线程下读写安全,因为写文件是后台线程,查询是API线程
private AtomicLong totalWrittenBytes = new AtomicLong(0);
  1. 在写文件的循环里,每次写完数据就把字节数累加到计数器:
while ((bytesRead = bufferedInputStream.read(buffer)) != -1) {
    fileOutputStream.write(buffer, 0, bytesRead);
    // 实时累加已写入的字节数
    totalWrittenBytes.addAndGet(bytesRead);
    
    long now = System.currentTimeMillis();
    if (now - lastUpdatedOn > 2 * 1000L) {
        fileOutputStream.flush();
        lastUpdatedOn = now;
    }
}
  1. 修改查询API的代码,根据写文件线程的状态返回对应值:
java.nio.file.Path path = Paths.get(destinationFullFolderPath.toString());
if (Files.exists(path)) {
    // 判断写文件的后台线程是否还在运行
    if (writeFileBackgroundThread.isAlive()) {
        // 还在写,直接返回内存计数器的实时值
        resultMap.put(fileName, totalWrittenBytes.get());
    } else {
        // 写完了,返回磁盘上的实际文件大小(也可以直接用计数器的值,因为最终两者相等)
        resultMap.put(fileName, Files.size(path));
    }
}

这个方案完全不会影响写文件的性能,而且能拿到100%实时的写入进度,非常适合大文件的场景。

方案二:优化写文件的刷盘逻辑(适合必须依赖磁盘文件大小的场景)

如果你一定要用Files.size()来获取大小,那就要修改写文件的代码,让数据更快地刷到磁盘:

  1. 缩短flush的时间间隔,比如从2秒改成100毫秒,但这样会稍微影响写文件的速度;
  2. 每次写入后主动调用flush,但这个对性能影响很大,大文件场景不推荐;
  3. 如果是Windows系统,还可以在flush之后加一行fileOutputStream.getFD().sync(),强制操作系统把数据写到磁盘,但这个开销极大,会让写文件速度大幅下降。

举个修改flush逻辑的例子:

while ((bytesRead = bufferedInputStream.read(buffer)) != -1) {
    fileOutputStream.write(buffer, 0, bytesRead);
    long now = System.currentTimeMillis();
    // 缩短flush间隔,比如500毫秒一次
    if (now - lastUpdatedOn > 500L) {
        fileOutputStream.flush();
        // 强制刷到磁盘(可选,性能损耗大)
        // fileOutputStream.getFD().sync();
        lastUpdatedOn = now;
    }
}

总结

优先选方案一,用内存计数器的方式,实时性和性能都能兼顾。方案二适合一些必须依赖磁盘文件大小的特殊场景,但会牺牲写文件的效率。

备注:内容来源于stack exchange,提问作者Amit J.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.13 20:20:29