首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >使用io.Copy(),稀疏文件很大

使用io.Copy(),稀疏文件很大
EN

Stack Overflow用户
提问于 2017-03-26 22:19:51
回答 2查看 1.5K关注 0票数 5

我想把文件从一个地方复制到另一个地方,问题是我处理了很多稀疏文件。

有什么(容易的)方法来复制稀疏文件而不会在目的地变得很大?

我的基本代码:

代码语言:javascript
运行
复制
out, err := os.Create(bricks[0] + "/" + fileName)
in, err := os.Open(event.Name)
io.Copy(out, in)
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2017-03-27 07:39:35

一些背景理论

请注意,io.Copy()使用原始字节--当您考虑到它将数据从io.Reader传输到相应地提供Read([]byte)Write([]byte)io.Writer时,这是可以理解的。因此,io.Copy()能够处理提供字节的任何源,绝对可以处理消耗字节的任何接收器。

另一方面,文件中漏洞的位置是一个“侧通道”信息,“经典”系统(如read(2) )对用户隐藏。io.Copy()无法以任何方式传递这样的侧通道信息.

IOW,最初,文件稀疏是一个想法,只是有一个有效的存储数据背后的用户。

因此,不,io.Copy()本身不可能处理稀疏文件。

该怎么办呢?

您需要更进一步,并使用syscall包和一些手工修改来实现所有这些。

要使用漏洞,您应该使用SEEK_HOLESEEK_DATA特殊值来处理lseek(2) syscall,这些值虽然在形式上是非标准的,但得到了 主修 平台的支持。

不幸的是,对那些“哪里”位置的支持既不存在于股票syscall包(截至Go 1.8.1)中,也不存在于golang.org/x/sys树中。

但是不要害怕,有两个简单的步骤:

  1. 首先,股票syscall.Seek()实际上是映射到相关平台上的lseek(2)
  2. 接下来,您需要为需要支持的平台确定SEEK_HOLESEEK_DATA的正确值。 请注意,它们是免费的,可以在不同平台之间成为不同的

比如说,在我的Linux系统上,我可以做一些简单的事情。

$ grep -E‘SEEK_(孔式数据)’

…计算出这些符号的值。

现在,比方说,在包中创建一个Linux特定的文件,其中包含以下内容

代码语言:javascript
运行
复制
// +build linux

const (
    SEEK_DATA = 3
    SEEK_HOLE = 4
)

然后将这些值与syscall.Seek()一起使用。

可以使用syscall.Seek()值的Fd()方法从打开的文件中获取传递给os.File和朋友的文件描述符。

读取时使用的模式是检测包含数据的区域,并从其中读取数据--参见中的一个示例。

请注意,这涉及到读取稀疏文件;但是如果您想要将它们作为稀疏传输--也就是说,保留它们的属性--情况就更复杂了:它的可移植性似乎更低,因此需要进行一些研究和实验。

在Linux上,您似乎可以尝试使用fallocate(2)FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE在您正在写入的文件的末尾打一个洞;如果这个错误(使用syscall.EOPNOTSUPP)合法地失败了,您只需像正在读取的洞中所覆盖的那样,将尽可能多的零块移到目标文件上--希望操作系统能做正确的事情,并将它们本身转换为一个洞。

请注意,一些文件系统根本不支持漏洞-作为一个概念。一个例子是FAT家族中的文件系统。我要介绍的是,在您的示例中,无法创建稀疏文件实际上可能是目标文件系统的一个属性。

您可能会发现“存档/ tar :添加对编写包含稀疏文件的tar的支持”是有兴趣的。

还要注意:您还可以考虑检查要复制源文件的目标目录是否驻留在与源文件相同的文件系统中,如果这是正确的,则使用syscall.Rename() (在POSIX系统上)或os.Rename()将文件移动到不同的目录w/o实际复制其数据。

票数 8
EN

Stack Overflow用户

发布于 2020-06-25 12:38:57

你不需要求助于系统。

代码语言:javascript
运行
复制
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"))
}

然后你会看到:

代码语言:javascript
运行
复制
$ 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中的那个部分。这个特定的实现留给读者做练习:)

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/43035271

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档