当写入大文件时,FileOutputStream.close非常缓慢。

5
我有一个方法,使用以下代码通过TCP套接字接收文件:
FileOutputStream fileStream = new FileOutputStream(filename.getName());
while (totalRead < size) {
    if (size - totalRead > CHUNKSIZE) {
        read = getInputStream().read(buffer, 0, CHUNKSIZE);
    } else {
        read = getInputStream().read(buffer, 0, size - totalRead);
    }
    totalRead += read;
    fileStream.write(buffer, 0, read);
    fileStream.flush();

    if (System.currentTimeMillis() > nextPrint) {
        nextPrint += 1000;
        int speed = (int) (totalRead / (System.currentTimeMillis() - startTime));
        double procent = ((double)totalRead / size) * 100;
        gui.setStatus("Reciving: " + filename + " at " + speed + " kb/s, " + procent + "% complete");
    }
}
gui.setStatus("Reciving: " + filename + " complete.");
fileStream.close();

当接收大文件时,FileOutputStream.close需要很长时间,为什么会这样?从你的代码中可以看出,在每个接收到的数据块都有进行流刷新操作。


“真正的长时间”是多久?如果在close()之前查看操作系统中的文件大小,它会显示什么? - Jon Skeet
当接收一个500Mb文件时,关闭操作大约需要30秒钟。我可以看到文件在接收时“增长”,因此它在每次刷新时都写入磁盘。 - simon
1
但是在调用close之前,它是否已经正确地得到了最终的大小?我猜它在操作系统级别上并没有真正地刷新,但我不能确定。 - Jon Skeet
你的复制循环比必要的要复杂得多。你只需要 'while ((count = in.read(buffer)) > 0) out,write(buffer, 0, count);'。 - user207421
2个回答

4
根据操作系统的不同,flush() 只是强制将数据写入操作系统。对于 FileOutputStream,write() 将所有数据传递给操作系统,因此 flush() 没有任何作用。而 close() 可以确保文件实际写入磁盘(或者根据操作系统而定可能不会写入)。您可以查看在写入数据时磁盘是否仍在忙碌。
一个 500 MB 的文件需要 30 秒,这意味着您正在写入 17 MB/s。这听起来像是一个非常慢的磁盘,或者该文件位于网络共享/驱动器上。
您可以尝试这个。
File file = File.createTempFile("deleteme", "dat"); // put your file here.
FileOutputStream fos = new FileOutputStream(file);
long start = System.nanoTime();
byte[] bytes = new byte[32 * 1024];
for (long l = 0; l < 500 * 1000 * 1000; l += bytes.length)
    fos.write(bytes);
long mid = System.nanoTime();
System.out.printf("Took %.3f seconds to write %,d bytes%n", (mid - start) / 1e9, file.length());
fos.close();
long end = System.nanoTime();
System.out.printf("Took %.3f seconds to close%n", (end - mid) / 1e9);

打印

Took 0.116 seconds to write 500,006,912 bytes
Took 0.002 seconds to close

从速度上可以看出,在这个系统上,即使在接近的情况下也没有写入数据,即硬盘速度不太快。


3
flush()FileOutputStream上没有作用,它只对缓冲流有用。 - WilQu
啊,这很有道理。当我在家尝试相同的代码时,close() 只花了不到一秒钟的时间,所以我猜在学校遇到 close() 花费 30 秒左右的“问题”,是因为网络挂载的家庭文件夹导致的。 - simon
2
不,FileOutputStream.close 无法保证所有字节都实际写入磁盘。但您可以保证所有字节都写入操作系统缓冲区。如果操作系统崩溃,例如停电,已经从 close() 返回的 FileOutputStream 可能会丢失数据。 - BlackJoker
1
如果您已经关闭了文件,程序可能会终止,除非出现其他问题,否则您不会丢失数据。 - Peter Lawrey

1

我发现使用 filestream 时有相同的情况。我发现如果你将文件以 readwrite 方式打开,它会将所有内容缓存起来,直到你关闭或处理为止才进行写入。Flush 没有写入。但是,如果你的写操作扩展了文件的大小,则会自动刷新。

只以写入方式打开会在每次写入时自动刷新。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接