目录

IPFS 文件系统

文件系统和 IPFS

因为以下几个原因,使用 IPFS 上的文件会跟你的日常习惯不一样:

  • 内容定位意味着当文件内容变化后,文件的内容标识(CID)会跟着变化。
  • 太大的文件可能无法放在一个数据块里,所以 IPFS 会把数据分割为多个块,使用元数据把它们链接在一起。

可变文件系统(MFS)和Unix文件系统(UnixFS)可以帮你理解这些文件管理的思路。

提示 如果你对 MFS 和 UnixFS在 IPFS 上如何工作非常感兴趣,可以观看来自 IPFS Camp 2019的视频。 核心课程: IPFS怎么处理文件?

可变文件系统 (MFS)

因为 IPFS 上的文件是基于文件内容定位而且不可更改,所以要修改它们操作就很复杂. 可变文件系统(MFS)就是一个构建在 IPFS 里面,让你可以像常规文件名文件系统一样操作文件的工具——你可以添加、删除、移动和编辑 MFS 文件,MFS 为您处理更新链接和哈希的所有工作。

使用 MFS 文件API

可通过 IPFS 命令行 和 API 中的文件命令进行访问 MFS。文件 API 上的命令采用ipfs.files.someMethod() 这样的格式。这些方法用于:

  • 创建目录
  • 检查目录状态
  • 向 MFS 添加文件
  • 查看目录内容
  • 复制目录或文件
  • 移动目录或文件
  • 读取文件内容
  • 删除目录或文件

更喜欢动手学习? 可以去 ProtoSchool的可变文件系统 MFS 函数教程试一下, 直接在您的 Web 浏览器中解决编码挑战。

创建目录

MFS 的 ipfs.files.mkdir 函数在指定路径处创建一个新目录。例如,将目录'/example'添加到根目录(/),运行:

1
await ipfs.files.mkdir('/example')

如果要尚不存在的其他目录下创建多层新目录,则需要将parents的值显式设置为 true,如下所示:

1
await ipfs.files.mkdir('/my/directory/example', { parents: true })

检查目录状态

ipfs.files.stat 方法使您能够检查 IPFS 节点上文件或目录的状态。若要检查位于根目录中的名为 example 的目录的状态,可以通过运行以下命令来调用该方法:

1
await ipfs.files.stat('/example')

此方法返回一个对象,该对象具有 cid, size, cumulativeSize, type, blocks, withLocality, local, and sizeLocal.

1
2
3
4
5
6
7
// {
//   hash: CID('QmXmJBmnYqXVuicUfn9uDCC8kxCEEzQpsAbeq1iJvLAmVs'),
//   size: 60,
//   cumulativeSize: 118,
//   blocks: 1,
//   type: 'directory'
// }

如果将文件添加、移动、复制或删除到目录中,则该目录的哈希值将随着每个文件的修改而更改.

添加文件到 MFS

要将文件添加到 IPFS,请使用 MFS ipfs.files.write 方法,方法是使用以下命令: To add a file to IPFS, use the MFS ipfs.files.write method using the command:

1
await ipfs.files.write(path, content, [options])

提示 通过把{ create: true } bool参数设置为true,此方法可以创建一个全新的文件,文件内容可以是多种格式。

要将名为 examplePic 的文件对象添加到根目录,您可以运行:

1
await ipfs.files.write('/example.jpg', examplePic, { create: true })

该方法没有返回值.

查看目录内容

若要检查 ipfs.files.write 方法是否已按预期工作,请使用 ipfs.files.ls 方法来检查 MFS 上的目录:

1
await ipfs.files.ls([path], [options])

该方法将默认列出你的目录(/)下内容 ,或者您可以选择指定要检查的特定路径:

1
await ipfs.files.ls('/example')

此方法为每个文件或目录生成一个对象数组,其中包含 nametypesizecidmodemtime等属性。如果要检查 /example 目录的内容,请运行:

1
await ipfs.files.ls('/example')

复制文件或目录

在可变文件系统(MFS)中,与传统文件系统一样,您可以将文件或目录复制到新位置,同时使其在源位置保持不变。使用方法如下:

1
>await ipfs.files.cp(...from, to, [options])

提示 此方法提供了两个格式设置选项,用于传递 from 参数:

  • 你的节点中一个已经存在的 MFS 文件或目录路径。 (例如: /my-example-dir/my-example-file.txt)
  • 你或其他节点中存储的 IPFS 文件或目录路径 (比如: /ipfs/QmWc7U4qGeRAEgtsyVyeW2CRVbkHW31nb24jFyks7eA2mF)

to 参数指定 MFS 目标路径, 这里有个 {create: true } 参数用来决定是否创建不存在的目录(包括父目录)。

你可以用这个函数来执行一些其他操作:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// 复制单个文件到某个目录
await ipfs.files.cp('/example-file.txt', '/destination-directory')
await ipfs.files.cp('/ipfs/QmWGeRAEgtsHW3ec7U4qW2CyVy7eA2mFRVbk1nb24jFyks', '/destination-directory')

// 复制多个文件到某个目录
await ipfs.files.cp('/example-file-1.txt', '/example-file-2.txt', '/destination-directory')
await ipfs.files.cp('/ipfs/QmWGeRAEgtsHW3ec7U4qW2CyVy7eA2mFRVbk1nb24jFyks',
 '/ipfs/QmWGeRAEgtsHW3jk7U4qW2CyVy7eA2mFRVbk1nb24jFyre', '/destination-directory')

// 复制一个目录到另外一个目录。
await ipfs.files.cp('/source-directory', '/destination-directory')
await ipfs.files.cp('/ipfs/QmWGeRAEgtsHW3ec7U4qW2CyVy7eA2mFRVbk1nb24jFyks', '/destination-directory')

移动文件或目录

MFS 允许你用 ipfs.files.mv 在不同目录之间移动文件:

1
await ipfs.files.mv(from, to, [options])

from 参数是你想移动内容的源目录,to参数是目标目录.

这个方法可以执行不同的操作:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// 移动一个文件到一个目录。
await ipfs.files.mv('/example-file.txt', '/destination-directory')

// 移动一个目录到另外一个目录
await ipfs.files.mv('/source-directory', '/destination-directory')

//使用源文件覆盖目标文件
await ipfs.files.mv('/source-file.txt', '/destination-file.txt')

//移动多个文件到目的目录
await ipfs.files.mv('/example-file-1.txt', '/example-file-2.txt', '/example-file-3.txt', '/destination-directory')

读取文件内容

ipfs.files.read 方法允许您读取或在缓冲区中显示文件的内容。该方法采用以下格式::

1
ipfs.files.read(path, [options])

path 参数是要读取文件的路径, 它必须指向文件而不是目录。

删除文件或目录

MFS 允许你用以下方法删除文件或目录:

1
await ipfs.files.rm(...paths, [options])

paths 参数是要删除的一个或多个文件.

默认情况下,如果尝试删除仍包含内容的目录,则请求将失败。要删除目录及其中包含的所有内容,您需要使用{recursive: true}参数。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// 删除一个文件
await ipfs.files.rm('/my/beautiful/file.txt')

// 删除多个文件
await ipfs.files.rm('/my/beautiful/file.txt', '/my/other/file.txt')

// 删除一个目录以及目录下的内容
await ipfs.files.rm('/my/beautiful/directory', { recursive: true })

// 删除一个空目录。非空目录会操作失败
await ipfs.files.rm('/my/beautiful/directory')

Unix文件系统 (UnixFS)

当您将文件添加到 IPFS 时,它可能太大而无法容纳在单个块中,因此它需要用元数据把所有块链接在一起。UnixFS 是一种基于google protobuf的格式,用于描述 IPFS 中的文件、目录和符号链接。此数据格式用于表示 IPFS 中的文件及其所有链接和元数据。UnixFS 创建链接对象的块(或块树)。

UnixFS 目前有 Javascript 和 Go 实现。这些实现的具有用于运行不同功能:

  • 数据格式: 把 UnixFS 对象的 serialization/deserialization 转成 protocol buffers 格式
  • Importer: 构建文件和目录的 DAG (有向无环图
  • Exporter: 导出 DAG(有向无环图

数据格式

在 UnixFS 的 v1 版本,数据格式用以下 protobuf 格式表示:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
message Data {
    enum DataType {
        Raw = 0;
        Directory = 1;
        File = 2;
        Metadata = 3;
        Symlink = 4;
        HAMTShard = 5;
    }

    required DataType Type = 1;
    optional bytes Data = 2;
    optional uint64 filesize = 3;
    repeated uint64 blocksizes = 4;
    optional uint64 hashType = 5;
    optional uint64 fanout = 6;
    optional uint32 mode = 7;
    optional UnixTime mtime = 8;
}

message Metadata {
    optional string MimeType = 1;
}

message UnixTime {
    required int64 Seconds = 1;
    optional fixed32 FractionalNanoseconds = 2;
}

此数据对象用于 UnixFS 中的所有非叶节点:

  • 对于由多个块组成的文件,Type字段将设置为Filefilesize字段将设置为文件中的总字节数,blocksizes将包含每个子节点文件大小的列表。

  • 对于由单个块组成的文件,Type字段将设置为Filefilesize将设置为文件中的总字节数,文件数据将存储在Data字段中。

UnixFS 还支持两个可选的元数据格式字段:

  • mode - 以数字表示文件权限。如果未指定,则此字段对于目录/HAMT(Hash Array Mapped Trie)分片 默认为 0755 ,对于所有其他类型(如果适用),此字段默认为 0644.

  • mtime - 是一个双元素结构体(纳秒),表示相对于 Unix 纪元 1970-01-01T00:00:00Z 的修改时间(以秒为单位)。

Importer

将文件导入 UnixFS 分为两个过程: 分块功能和布局功能。您可以使用 IPFS DAG 生成器测试这些功能。

分块

将对象添加到 IPFS 时,该对象将被分块为小块,对每个块进行哈希处理,并为每个块创建一个 CID。此 DAG 生成过程有两个主要参数:叶格式和分块策略。

叶子格式有两个格式选项,UnixFS 叶(UnixFS leaves)和原始叶(raw leaves):

  • UnixFS leaves格式,在新添加的对象上添加数据包装器,以生成具有额外数据大小的 UnixFS 叶。此包装器用于确定新添加的对象是文件还是目录。此格式是 CID v0版本的默认格式。 UnixFS leaves format adds a data wrapper on newly added objects to produce UnixFS leaves with additional data sizes. This wrapper is used to determine whether newly added objects are files or directories. This format is the default for CIDv0.

  • Raw leaves 格式,其中分块输出的节点将是来自CID编解码器为"raw"的文件的原始数据。这主要是为了向后兼容使用 UnixFS 数据对象的格式。此格式是使用 ipfs 添加 –cid 版本 1 创建的 CIDv1 的默认格式,很快就会成为全局默认格式。the raw leaves format on IPFS where nodes output from chunking will be raw data from the file with a CID codec of ‘raw’. This is mainly configured for backward compatibility with formats that used a UnixFS Data object. This format is the default for CIDv1 created with ipfs add –cid-version 1, soon to become the global default.

分块策略用于确定分块过程中可用的大小选项。该策略目前有两种不同的选项,fixed sizerabin

  • fixed size分块策略: 将输入数据分割为设定大小的数据块。这可能是 512 字节、1024 字节等 , 字节大小越小,重复数据删除效果就越好。

  • Rabin分块策略: 使用 Rabin 指纹识别确定区块之间的边界,对输入数据进行分块。Rabin 还减少了输入数据分块节点的数量。

布局(Layout)

Layout定义从输入文件块生成的树的形状。

目前有两种Layout选项,balancedtrickle。此外,必须指定最大宽度。默认最大宽度为 174。

balanced布局创建宽度最大宽度的平衡树。该树是通过从块流中获取最大宽度的块并创建链接到所有这些块的UnixFS文件节点而形成的。重复此操作,直到创建最大宽度 UnixFS 文件节点,此时将创建一个 UnixFS 文件节点以递归方式保存所有这些节点。生成的树的根节点将作为新导入文件的句柄返回。 The balanced layout creates a balanced tree of width max-width. The tree is formed by taking up to max-width chunks from the chunk stream and creating a UnixFS file node that links to all of them. This is repeated until max-width UnixFS file nodes are created, at which point a UnixFS file node is created to hold all of those nodes recursively. The root node of the resultant tree is returned as the handle to the newly imported file.

如果只有一个区块,则不会创建中间 UnixFS 文件节点,并且单个区块将作为文件的句柄返回。 If there is only a single chunk, no intermediate UnixFS file nodes are created, and the single chunk is returned as the handle to the file.

Exporter

要从 UnixFS 图中导出或读取文件数据,请按顺序执行遍历,发出每个叶子中包含的数据。 To export or read the file data out of the UnixFS graph, perform an in-order traversal, emitting the data contained in each of the leaves.

更多资源

您可以在以下位置找到其他资源来熟悉这些文件系统: