我想将文件从一个地方复制到另一个地方,但问题是我要处理很多稀疏文件。
有没有什么(简单的)方法可以复制稀疏文件而不会在目的地变得很大?
我的基本代码:
out, err := os.Create(bricks[0] + "/" + fileName)
in, err := os.Open(event.Name)
io.Copy(out, in)
最佳答案
一些背景理论
请注意,io.Copy()
会通过管道传输原始字节——一旦您认为它将数据从 io.Reader
传输到 ,这就可以理解了io.Writer
对应提供了Read([]byte)
和Write([]byte)
。
因此,io.Copy()
绝对能够处理任何提供的源
字节和绝对消耗它们的任何接收器。
另一方面,文件中漏洞的位置是一种“旁道”信息,“经典”系统调用(例如 read(2)
)向用户隐藏了这些信息。
io.Copy()
无法以任何方式传达此类边信道信息。
IOW,最初,文件稀疏性只是在用户背后高效存储数据的想法。
所以,不,io.Copy()
本身无法处理稀疏文件。
怎么办
您需要更深入地使用 syscall
包和一些手动修补来实现所有这些。
要处理漏洞,您应该为 lseek(2)
系统调用使用 SEEK_HOLE
和 SEEK_DATA
特殊值,虽然正式非标准,由 all 支持major platforms .
不幸的是,不存在对那些“where”位置的支持
既不在股票 syscall
包中(从 Go 1.8.1 开始)
也不在 golang.org/x/sys
树。
但不要害怕,有两个简单的步骤:
首先,股票
syscall.Seek()
实际上映射到lseek(2)
在相关平台上。接下来,您需要计算出
SEEK_HOLE
的正确值,并且SEEK_DATA
用于您需要支持的平台。Note that they are free to be different between different platforms!
比如说,在我的 Linux 系统上我可以做一些简单的事情
$ grep -E 'SEEK_(HOLE|DATA)' </usr/include/unistd.h # define SEEK_DATA 3 /* Seek to next data. */ # define SEEK_HOLE 4 /* Seek to next hole. */
…计算出这些符号的值。
现在,比方说,你在你的包中创建了一个特定于 Linux 的文件 包含类似的东西
// +build linux
const (
SEEK_DATA = 3
SEEK_HOLE = 4
)
然后将这些值与 syscall.Seek()
一起使用。
要传递给 syscall.Seek()
和 friend 的文件描述符
可以使用 Fd()
方法从打开的文件中获取
os.File
值。
读取时使用的模式是检测包含数据的区域,并从中读取数据——参见 this举个例子。
请注意,这涉及读取稀疏文件;但是如果你真的想将它们传输为稀疏的——也就是说,在保持它们的这个属性的情况下——情况就更复杂了:它似乎更不便携,所以一些研究和实验到期了。
在 Linux 上,您似乎可以尝试使用 fallocate(2)
和
FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE
尝试在
您正在写入的文件的末尾;如果那合法地失败了
(使用 syscall.EOPNOTSUPP
),您只需将您正在读取的漏洞所覆盖的尽可能多的归零 block 铲到目标文件中——希望如此
操作系统会做正确的事情,并自行将它们变成一个洞。
请注意,一些文件系统根本不支持空洞——作为一个概念。 一个例子是 FAT 家族中的文件系统。 我要引导您的是,无法创建稀疏文件可能 在您的情况下实际上是目标文件系统的属性。
您可能会找到 Go issue #13548 "archive/tar: add support for writing tar containing sparse files"引起兴趣。
请注意:您还可以考虑检查复制源文件的目标目录是否与源文件位于同一文件系统中,如果是这样,请使用 syscall.Rename()
(在 POSIX 系统上)
或 os.Rename()
只在不同目录之间移动文件 w/o
实际上是在复制它的数据。
关于file - io.Copy() 的稀疏文件很大,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56270359/