我有以下测试代码:
try {
InputStream is;
Stopwatch.start("FileInputStream");
is = new FileInputStream(imageFile.toFile());
is.skip(1024*1024*1024);
is.close();
Stopwatch.stop();
Stopwatch.start("Files.newInputStream");
is = Files.newInputStream(imageFile);
is.skip(1024*1024*1024);
is.close();
Stopwatch.stop();
}
catch(Exception e)
{
}
我有以下输出:
Start: FileInputStream
FileInputStream : 0 ms
Start: Files.newInputStream
Files.newInputStream : 3469 ms
你知道发生了什么吗?为什么在第二种情况下跳过这么慢?
我需要使用从频道获取的InputStreams,因为我的测试表明我的任务最好是同时从文件读取两个线程(我只能在使用来自频道的Streams时注意到任何改进)。
在测试期间,我发现我可以做这样的事情:
SeekableByteChannel sbc = Files.newByteChannel(imageFile);
sbc.position(1024*1024*1024);
is = Channels.newInputStream(sbc);
只需要平均28ms,但这对我没有多大帮助,因为使用它我必须进行重大的API更改。
我的平台:
Linux galileo 3.11.0-13-generic #20-Ubuntu SMP Wed Oct 23 07:38:26 UTC 2013 x86_64 x86_64 x86_64 GNU/Linux
java version "1.7.0_45"
Java(TM) SE Runtime Environment (build 1.7.0_45-b18)
Java HotSpot(TM) 64-Bit Server VM (build 24.45-b08, mixed mode)
答案 0 :(得分:1)
查看源代码,skip()
的默认实现似乎实际上正在读取(并丢弃)流内容,直到达到目标位置:
public long skip(long n) throws IOException {
long remaining = n;
int nr;
if (n <= 0) {
return 0;
}
int size = (int)Math.min(MAX_SKIP_BUFFER_SIZE, remaining);
byte[] skipBuffer = new byte[size];
while (remaining > 0) {
nr = read(skipBuffer, 0, (int)Math.min(size, remaining));
if (nr < 0) {
break;
}
remaining -= nr;
}
return n - remaining;
}
SeekableByteChannel#position()
方法可能只更新偏移指针,实际上并不需要任何I / O.据推测,FileInputStream
会使用类似的优化覆盖skip()
方法。该文档支持这一理论:
此方法可能会跳过比备份文件中剩余的更多字节。这不会产生异常,跳过的字节数可能包括超出后备文件EOF的一些字节数。跳过结束后尝试从流中读取将导致-1表示文件结束。
在盘片磁盘或网络存储上,这可能会产生重大影响。
答案 1 :(得分:0)
尝试使用GetObjectRequest.setRange
设置范围,使其与skip
具有相同的行为。
GetObjectRequest req = new GetObjectRequest(BUCKET_NAME, "myfile.zip");
req.setRange(1024); // start download skiping 1024 bytes
S3ObjectInputStream in = client.getObject(req).getObjectContent();
// read "in" while not eof
我用它来避免我的实现SocketTimeoutException
。
每次收到SocketTimeoutException
时,我都会使用setRange
重新开始下载,以跳过我已下载的字节。