问题描述
谁能告诉我为什么 1. run 是错误的? (返回码为0,但写入的文件只有原来的一半。
提前致谢!
public class FilecopyFisFos {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("d:/Test1/OrigFile.MP4");
FileOutputStream fos = new FileOutputStream("d:/Test2/DestFile.mp4");
// 1. run
// while (fis.read() != -1){
// int len = fis.read();
// fos.write(len);
// }
// 2. run
// int len;
// while ((len = fis.read()) != -1){
// fos.write(len);
// }
fis.close();
fos.close();
}
}
解决方法
FileInputStream
的 read()
方法遵循以下逻辑:
从此输入流中读取一个字节的数据。如果尚无可用输入,则此方法会阻塞。
所以将其返回的值赋值给一个变量,如:
while((len = fis.read())!= -1)
避免刚刚从流中读取的数据字节被遗忘,因为每个 read()
调用都将分配给您的 len
变量。
相反,此代码会绕过流中每两个字节之一,因为在 read()
条件中执行的 while
从未分配给变量。因此流前进而没有读取一半的字节(分配给 len
):
while (fis.read() != -1) { // reads a byte of data (but not saved)
int len = fis.read(); // next byte of data saved
fos.write(len); // possible -1 written here
}
,
@aran 和其他人已经指出了您问题的解决方案。
然而,这有更多方面,所以我扩展了你的例子:
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class FileCopyFisFos {
public static void main(final String[] args) throws IOException {
final File src = new File("d:/Test1/OrigFile.MP4");
final File sink = new File("d:/Test2/DestFile.mp4");
{
final long startMS = System.currentTimeMillis();
final long bytesCopied = copyFileSimple(src,sink);
System.out.println("Simple copy transferred " + bytesCopied + " bytes in " + (System.currentTimeMillis() - startMS) + "ms");
}
{
final long startMS = System.currentTimeMillis();
final long bytesCopied = copyFileSimpleFaster(src,sink);
System.out.println("Simple+Fast copy transferred " + bytesCopied + " bytes in " + (System.currentTimeMillis() - startMS) + "ms");
}
{
final long startMS = System.currentTimeMillis();
final long bytesCopied = copyFileFast(src,sink);
System.out.println("Fast copy transferred " + bytesCopied + " bytes in " + (System.currentTimeMillis() - startMS) + "ms");
}
System.out.println("Test completed.");
}
static public long copyFileSimple(final File pSourceFile,final File pSinkFile) throws IOException {
try (
final FileInputStream fis = new FileInputStream(pSourceFile);
final FileOutputStream fos = new FileOutputStream(pSinkFile);) {
long totalBytesTransferred = 0;
while (true) {
final int readByte = fis.read();
if (readByte < 0) break;
fos.write(readByte);
++totalBytesTransferred;
}
return totalBytesTransferred;
}
}
static public long copyFileSimpleFaster(final File pSourceFile,final File pSinkFile) throws IOException {
try (
final FileInputStream fis = new FileInputStream(pSourceFile);
final FileOutputStream fos = new FileOutputStream(pSinkFile);
BufferedInputStream bis = new BufferedInputStream(fis);
BufferedOutputStream bos = new BufferedOutputStream(fos);) {
long totalBytesTransferred = 0;
while (true) {
final int readByte = bis.read();
if (readByte < 0) break;
bos.write(readByte);
++totalBytesTransferred;
}
return totalBytesTransferred;
}
}
static public long copyFileFast(final File pSourceFile,final File pSinkFile) throws IOException {
try (
final FileInputStream fis = new FileInputStream(pSourceFile);
final FileOutputStream fos = new FileOutputStream(pSinkFile);) {
long totalBytesTransferred = 0;
final byte[] buffer = new byte[20 * 1024];
while (true) {
final int bytesRead = fis.read(buffer);
if (bytesRead < 0) break;
fos.write(buffer,bytesRead);
totalBytesTransferred += bytesRead;
}
return totalBytesTransferred;
}
}
}
该代码附带的提示:
- java.nio 包通常可以更快地完成这些事情,并且代码更少。
- 复制单个字节比批量复制慢 1'000-40'000 倍。
- 使用 try/resource/catch 是避免文件等保留/锁定资源出现问题的最佳方法。
- 如果您解决了一些很常见的问题,我建议您将其放在您自己的实用程序类中,甚至是您自己的库中。
- 有像 BufferedInputStream 和 BufferedOutputStream 这样的辅助类,可以极大地提高效率;请参见示例 copyFileSimpleFaster()。
- 但与往常一样,概念的质量对实施的影响最大;参见示例 copyFileFast()。
- 还有更高级的概念(类似于 java.nio),其中考虑了操作系统缓存行为等概念,这将进一步提升性能。
检查我的输出,或自行运行,以查看性能差异:
Simple copy transferred 1608799 bytes in 12709ms
Simple+Fast copy transferred 1608799 bytes in 51ms
Fast copy transferred 1608799 bytes in 4ms
Test completed.