使用零拷贝对文件高效的切片和合并
对文件的切片/合并在应用中是一个很常见的需求,使用 FileChannel的 transferTo / transferFrom 的零拷贝方法(需要操作系统支持),可以高效的完成。
切片
public static void chunkFile(Path file
, long chunkSize
) throws IOException
{
if (Files
.notExists(file
) || Files
.isDirectory(file
)) {
throw new IllegalArgumentException("文件不存在:" + file
);
}
if (chunkSize
< 1) {
throw new IllegalArgumentException("分片大小不能小于1个字节:" + chunkSize
);
}
final long fileSize
= Files
.size(file
);
final long numberOfChunk
= fileSize
% chunkSize
== 0 ? fileSize
/ chunkSize
: (fileSize
/ chunkSize
) + 1;
final String fileName
= file
.getFileName().toString();
try(FileChannel fileChannel
= FileChannel
.open(file
, EnumSet
.of(StandardOpenOption
.READ
))){
for (int i
= 0; i
< numberOfChunk
; i
++) {
long start
= i
* chunkSize
;
long end
= start
+ chunkSize
;
if (end
> fileSize
) {
end
= fileSize
;
}
Path chunkFile
= Paths
.get(fileName
+ "-" + (i
+ 1));
try (FileChannel chunkFileChannel
= FileChannel
.open(file
.resolveSibling(chunkFile
),
EnumSet
.of(StandardOpenOption
.CREATE_NEW
, StandardOpenOption
.WRITE
))){
fileChannel
.transferTo(start
, end
- start
, chunkFileChannel
);
}
}
}
}
组合
public static void mergeFile
(Path file
, Path
... chunkFiles
) throws IOException
{
if (chunkFiles
== null
|| chunkFiles
.length
== 0) {
throw new IllegalArgumentException("分片文件不能为空");
}
try (FileChannel fileChannel
= FileChannel
.open(file
, EnumSet
.of(StandardOpenOption
.CREATE_NEW
, StandardOpenOption
.WRITE
))){
for (Path chunkFile
: chunkFiles
) {
try(FileChannel chunkChannel
= FileChannel
.open(chunkFile
, EnumSet
.of(StandardOpenOption
.READ
))){
chunkChannel
.transferTo(0, chunkChannel
.size(), fileChannel
);
}
}
}
}
最后
零拷贝
https://zh.wikipedia.org/zh-hans/零复制