我想将文件从一个地方复制到另一个地方,问题是我要处理很多稀疏文件。
是否有任何(简单)方法可以复制稀疏文件而不会在目的地变得庞大?
我的基本代码:
out, err := os.Create(bricks[0] + "/" + fileName)
in, err := os.Open(event.Name)
io.Copy(out, in)
我想将文件从一个地方复制到另一个地方,问题是我要处理很多稀疏文件。
是否有任何(简单)方法可以复制稀疏文件而不会在目的地变得庞大?
我的基本代码:
out, err := os.Create(bricks[0] + "/" + fileName)
in, err := os.Open(event.Name)
io.Copy(out, in)
请注意,io.Copy()
管道原始字节 - 一旦您认为它将数据从 an 管道传输io.Reader
到 an io.Writer
which 提供Read([]byte)
和Write([]byte)
相应地,这是可以理解的。因此,io.Copy()
绝对能够处理任何提供字节的源和绝对任何消耗它们的接收器。
另一方面,文件中漏洞的位置是“旁通道”信息,“经典”系统调用(例如read(2)
对用户隐藏)。
io.Copy()
无法以任何方式传达此类旁道信息。
IOW,最初,文件稀疏是一个想法,只是在用户背后有效地存储数据。
所以,不,没有办法io.Copy()
处理稀疏文件本身。
您需要更深一层并使用syscall
包和一些手动修补来实现所有这些。
要处理漏洞,您应该使用系统调用的SEEK_HOLE
和SEEK_DATA
特殊值lseek(2)
,虽然它们在形式上是非标准的,但受到所有 主要 平台的支持。
syscall
不幸的是,stock包(从 Go 1.8.1 开始)和golang.org/x/sys
树中都没有对这些“来源”位置的支持。
但不要害怕,有两个简单的步骤:
首先,股票syscall.Seek()
实际上映射到lseek(2)
相关平台上。
接下来,您需要为您需要支持的平台SEEK_HOLE
找出
正确的值。SEEK_DATA
请注意,它们可以在不同平台之间自由变化!
说,在我的 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()
从打开的文件中获取。Fd()
os.File
读取时使用的模式是检测包含数据的区域,并从中读取数据 -举一个例子。
请注意,这涉及读取稀疏文件;但是,如果你想真正将它们作为稀疏传输——也就是说,保留它们的这个属性——情况会更复杂:它似乎更不便携,所以需要进行一些研究和实验。
在 Linux 上,您似乎可以尝试使用fallocate(2)
with
FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE
在您正在写入的文件末尾打一个洞;如果这合法地失败了(使用syscall.EOPNOTSUPP
),您只需将尽可能多的零块铲到目标文件中,就像您正在读取的孔所覆盖的那样——希望操作系统会做正确的事情并自行将它们转换为孔。
请注意,某些文件系统根本不支持漏洞——作为一个概念。一个例子是 FAT 系列中的文件系统。我要引导您的是,在您的情况下,无法创建稀疏文件实际上可能是目标文件系统的一个属性。
您可能会感兴趣的 Go 问题 #13548 “archive/tar:添加对编写包含稀疏文件的 tar 的支持”。
另一个注意事项:您可能还考虑检查复制源文件的目标目录是否与源文件位于同一文件系统中,如果这是真的,请使用syscall.Rename()
(在 POSIX 系统上)或os.Rename()
只是将文件移动到不同的目录没有实际复制其数据。
您不需要求助于系统调用。
package main
import "os"
func main() {
f, _ := os.Create("/tmp/sparse.dat")
f.Write([]byte("start"))
f.Seek(1024*1024*10, 0)
f.Write([]byte("end"))
}
然后你会看到:
$ ls -l /tmp/sparse.dat
-rw-rw-r-- 1 soren soren 10485763 Jun 25 14:29 /tmp/sparse.dat
$ du /tmp/sparse.dat
8 /tmp/sparse.dat
确实,您不能io.Copy
按原样使用。相反,您需要实现一个替代方案,io.Copy
从. 如果是,只需跳过. 该特定实现留给读者作为练习:)src
'\0'
dst.Seek(len(chunk), os.SEEK_CUR)
dst