Java获取文件实时大小的问题咨询
Java获取文件实时大小的问题咨询
你好,我来帮你分析下这个问题哈!
首先,你遇到的核心问题是:写文件的后台进程在持续写入,但查询文件大小的API只有等整个文件写完才返回数值,之前一直拿到空/0的结果。咱们先搞清楚原因,再给你具体的解决办法。
为什么当前查询API拿不到实时大小?
你的查询代码本身是没问题的,Files.size()确实能获取文件的实时大小,但有个前提:已经写入的数据必须被刷到磁盘上。
看你的写文件代码,用了BufferedOutputStream做缓冲,而且只有当两次写入间隔超过2秒的时候才会调用flush()。这就导致:
- 在两次flush的间隔内,写入的数据都存在内存缓冲区里,根本没写到磁盘,这时候
Files.size()读的是磁盘上的文件,自然拿不到这部分数据的大小; - 哪怕你触发了flush,有些操作系统还会把数据存在自己的磁盘缓冲区里,没有真正落地到磁盘,这时候
Files.size()也可能拿不到最新值; - 直到整个文件写完,try-with-resources自动关闭流的时候,才会强制把所有缓冲区的数据刷到磁盘,这时候你才能拿到完整的文件大小。
具体解决办法(推荐方案优先)
方案一:维护内存计数器(推荐,实时性+性能兼顾)
这个办法不需要改查询API的核心逻辑,而是在写文件的代码里加一个线程安全的计数器,实时记录已经写入的字节数。查询的时候直接读这个计数器,不用等数据刷到磁盘,既快又准。
步骤如下:
- 在你跑后台写文件的类里,加一个线程安全的计数器:
// AtomicLong保证多线程下读写安全,因为写文件是后台线程,查询是API线程 private AtomicLong totalWrittenBytes = new AtomicLong(0);
- 在写文件的循环里,每次写完数据就把字节数累加到计数器:
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; } }
- 修改查询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()来获取大小,那就要修改写文件的代码,让数据更快地刷到磁盘:
- 缩短flush的时间间隔,比如从2秒改成100毫秒,但这样会稍微影响写文件的速度;
- 每次写入后主动调用flush,但这个对性能影响很大,大文件场景不推荐;
- 如果是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.
相关产品推荐
相关产品推荐

