IPFS 星际文件系统
IPFS(Interplanetary File System,星际文件系统),是点对点(peer-to-peer,简称 p2p)文件共享系统。
IPFS = 公私钥 + Git + bit下载
IPFS 本质上是版本化
的分布式文件系统,能够接收文件并管理它们,也可以把它们存储在某个地方,然后随着时间的推移,跟踪它们的版本。IPFS 也记录了这些文件在网络中的移动方式,因此,它也是个分布式文件系统。
IPFS组成部分
分布式哈希表DHT(承担路由功能)
代码演示:https://gitee.com/wujian2023/go-libp2p-dht-demo.git
哈希表 是一种数据结构,它以键值对来存储信息。
在分布式哈希表(distributed hash tables,简称 DHT)中,数据分布在计算机网络中,以便有效地协调以实现节点之间的有效访问和查找。
DHT 的主要优点在于去中心化、容错和可扩展性。节点无需中心协调,系统能够可靠地运作,即使节点发生故障或下线。并且,DHT 能够扩展以容纳数百万个节点。基于这些特性,使得 DHT比中央服务器的结构更具有弹性。
分布式散列表本质上强调以下特性:
- 离散性:构成系统的节点并没有任何中央式的协调机制。
- 伸缩性:即使有成千上万个节点,系统仍然应该十分有效率。
- 容错性:即使节点不断地加入、离开或是停止工作,系统仍然必须达到一定的可靠度。
DHT的结构可以分成几个主要的组件。其基础是一个抽象的键空间
(keyspace),160位长的字符串集合。键空间分区
(keyspace partitioning)将键空间
分区成数个,并指定到在此系统的节点中。
延展网络
则连接这些节点,并让他们能够借由在键空间
内的任一值找到拥有该值的节点。
基本步骤
文件名称为filename
且内容为data
存储:
-
键值
k=SHA(filename)
-
put(k,data)
会在延展网络
中被路由,抵达在键空间
分区中被指定负责存储关键值k
的节点(一般是多个) - 节点保存
(k,data)
取:
-
键值
k=SHA(filename)
-
get(k)
会取找到存储关键值k
的节点 - 此节点会相应请求,回传数据
路由算法的特点
基本上,就是一种映射key和节点的算法以及路由的算法。
其一为保证任何的路由路径长度必须尽量短,因而请求能快速地被完成; 其二为任一节点的邻近节点数目(又称最大节点度(Degree (graph theory)))必须尽量少,因此维护的花费不会过多。
Kademlia算法
(这个算法感觉比较像递归分块,把网络分成很多部分,然后与每个部分的部分节点保持联系)
Kademlia
是一种通过 DHT 的协议算法,它是由Petar和David在2002年为P2P网络而设计的。Kademlia
规定了网络的结构,也规定了通过节点查询进行信息交换的方式。
Kademlia
网络节点之间使用UDP
进行通讯。参与通讯的所有节点形成一张虚拟网(或者叫做覆盖网)。这些节点通过一组数字(或称为节点ID)来进行身份标识。节点ID不仅可以用来做身份标识,还可以用来进行值定位(值通常是文件的散列或者关键词)。
当我们在网络中搜索某些值(即通常搜索存储文件散列或关键词的节点)的时候,Kademlia
算法需要知道与这些值相关的键,然后逐步在网络中开始搜索。每一步都会找到一些节点,这些节点的ID与键更为接近,如果有节点直接返回搜索的值或者再也无法找到与键更为接近的节点ID的时候搜索便会停止。
这种搜索值的方法是非常高效的:与其他的分布式哈希表的实现类似,在一个包含n个节点的系统的值的搜索中,Kademlia
仅访问O(log(n))
个节点。
Kademlia
简称为Kad
,它使用了一个精妙的算法,来计算节点之间的"距离" (这里的距离不是地理空间的距离,而是路由的跳数),这个算法就是XOR
操作(异或),因为这个操作和距离的计算类似:
(A ⊕ B) == (B ⊕ A)
: XOR 符合“交换律”,具备对称性。A和B的距离从哪一个节点计算都是相同的。(A ⊕ A) == 0
: 反身性,自己和自己的距离为零。(A ⊕ B) > 0
: 两个不同的 key 之间的距离必大于零。(A ⊕ B) + (B ⊕ C) >= (A ⊕ C)
: 三角不等式, A经过B到C的距离总是大于A直接到C的距离。(关键)
Kademlia
使用160位的哈希算法(比如 SHA1),完整的 key 用二进制表示有160位,这样可以容纳2160个节点,可以说是不计其数了。
Kademlia
把 key 映射到一个二叉树,每一个 key 都是这个二叉树的叶子
。
映射规则
- 先把 key 以二进制形式表示,然后从高位到低位依次处理。
- 二进制的第 n 个位就对应了二叉树的第 n 层
- 如果该位是1,进入左子树,是0则进入右子树(这只是人为约定,反过来处理也可以)
- 全部位都处理完后,这个 key 就对应了二叉树上的某个叶子
二叉树的拆分规则
对每一个节点,都可以按照自己的视角对整个二叉树进行拆分成最多160个子树。
拆分的规则是:先从根节点开始,把不包含自己的那个子树拆分出来;然后在剩下的子树再拆分不包含自己的第二层子树;以此类推,直到最后只剩下自己。
Kademlia
默认的散列值空间是 m=160
(散列值有 160 bit),因此拆分出来的子树有 160 个。
对于每一个节点而言,当它以自己的视角完成子树拆分后,会得到 n 个子树;对于每个子树,如果它都能知道里面的一个节点,那么它就可以利用这 n 个节点进行递归路由,从而到达整个二叉树的任何一个节点。
key-value的key会保存到节点key附近的子网中
节点的key,下面会保存很多的key-value
key-value[byte]的key会尽量与节点的key距离相近,两个key异或的结果比较小
K-桶(*)
每个节点在完成子树拆分后,只需要知道每个子树里面的一个节点,就足以实现全遍历。但是考虑到健壮性(节点可能宕机或者退出),光知道一个显然是不够的,需要知道多个才比较保险。
所以 Kademlia
论文中给出了一个K-桶(K-bucket)
的概念。
Kademlia
中使用了名为K-桶
的概念来储存其他(临近)节点的状态信息,这里的状态信息主要指的就是节点ID, IP, 和端口。对于160bit的节点ID,就有160个K-桶
,对于每一个K-桶
i,它会储存与自己距离在区间 [2^i, 2^(i+1)) 范围内的节点的信息,每个K-桶中储存有k个其他节点的信息,在BitTorrent的实现中,k的取值为8。
下表反映了每个K-桶所储存的信息
K-桶 | 储存的距离区间 | 储存的距离范围 | 储存比率 |
---|---|---|---|
0 | [1, 2) | 1 | 100% |
1 | [2, 4) | 2-3 | 100% |
2 | [4, 8) | 4-7 | 100% |
3 | [8, 16) | 8-15 | 100% |
4 | [16,32) | 16-31 | 75% |
5 | [32, 64) | 32-63 | 57% |
10 | [1024, 2048) | 1024-2047 | 13% |
i | [2^i, 2^i+1) | / | 0.75i-3 |
放在节点树上,即每个节点都更倾向于储存与自己距离近的节点的信息,形成储存的离自己近的节点多, 储存离自己远的节点少的局面。
对于一个节点而言,K-桶
就代表着节点树上那些未知的节点(其实除了自己都是未知的)构成的子树,160个K桶分别是具有1到160层的子树,由小至大。对于节点ID,160个K-桶分别储存着节点ID前0到159个bit和自己一致的节点。
K-桶中的条目(其他临近节点的状态信息)排序的,每当收到一个消息(如查询)时,就要更新一次K桶。 首先计算自己与对方的距离,然后储存到对应的K-桶中,但如果K-桶已满(前面提到每个K-桶储存有k=8个条目),则会倾向放弃储存,继续保持旧的节点信息(如果还有效的话). 除了距离外,Kademlia更倾向于与在线时间长,稳定的节点建立联系。
这是因为实践证明,累积在线时间越长的节点越稳定,越倾向于继续保持在线,即节点的失效概率和在线时长成反比。
这样还可以在一定程度上抵御攻击行为。因为当大量恶意的新节点涌入时,大家都会选择继续保持旧的节点信息,而不是接受新的。
除此之外,还需要定时检查K-桶中的节点是否任然在线,及时删去已失效节点。
K-桶
其实就是路由表。对于某个节点而言,如果以它自己为视角拆分了 n 个子树,那么它就需要维护 n 个路由表,并且每个路由表的上限是 K
。
四种消息
Kademlia
协议共有四种消息。
- PING消息: 用来测试节点是否仍然在线。
- STORE消息: 在某个节点中存储一个键值对。
- FIND_NODE消息: 消息请求的接收者将返回自己桶中离请求键值最近的K个节点。
- FIND_VALUE消息: 与FIND_NODE一样,不过当请求的接收者存有请求者所请求的键的时候,它将返回相应键的值。
因为每个节点都更倾向于储存距自己近的节点的信息,而整个网络又是一个二叉树,因此每次查询都会至少取得距离减半的结果,对于有N个节点的网络,至多只需要 log2N
次查询。
当进行 FIND VALUE 操作时,查询操作是类型的,每份数据都有一个同样是160bit的键,每份数据都倾向于储存在与键值距离较近的节点上。
当上传者上传一份数据时,上传者首先定位几个较为接近键值的节点,用STORE操作要求他们储存这份数据。
储存有数据的节点,每当发现比自己与键值距离更为接近的节点时,便将数据复制一份到这个节点上。
当一个新节点欲加入网络时,只需找到一个已在网络中的节点,借助它对自己的节点ID进行一次常规查询即可,这样便完成了对自己信息的广播,让距自己较近的节点获知自己的存在。而离开网络不必执行任何操作,一段时间后,你的信息会自动地从其他节点被删除。
定位节点
节点查询可以异步进行,也可以同时进行,同时查询的数量由α表示,一般是3。
(像dns的迭代查询方法)
- 由查询发起者从自己的k-桶中筛选出若干距离目标ID最近的节点,并向这些节点同时发送异步查询请求;
- 被查询节点收到请求之后,将从自己的k-桶中找出自己所知道的距离查询目标ID最近的若干个节点,并返回给发起者;
- 发起者在收到这些返回信息之后,更新自己的结果列表,再次从自己所有已知的距离目标较近的节点中挑选出若干没有请求过的,并重复步骤1;
- 上述步骤不断重复,直至无法获得比查询者当前已知的k个节点更接近目标的活动节点为止。
- 在查询过程中,没有及时响应的节点将立即被排除;查询者必须保证最终获得的k个最节点都是活动的。
举个例子:
节点 0要call节点13:
正常情况下,节点0拥有4个K-桶
,K-桶0
存的节点1,K-桶1
存节点2-3,K-桶2
存节点4-7,K-桶3
存节点8-15
假设与节点0向连的是节点1,2,4,8 计算:1 XOR 13 = 12,2 XOR 13 = 15,4 XOR 13 = 9,8 XOR 13 = 5
选XOR结果最小的节点来连接,故,0和8连接
与节点8连接的是节点9,10,12 计算:9 XOR 13 = 4,10 XOR 13 = 7,12 XOR 13 = 1,
选择12与13相连
(实际是迭代的过程,图画的比较像递归查询)
定位资源
通过把资源信息与键进行映射,资源即可进行定位,哈希表是典型的用来映射的手段。由于以前的STORE消息,存储节点将会有对应STORE所存储的相关资源的信息。定位资源时,如果一个节点存有相应的资源的值的时候,它就返回该资源,搜索便结束了,除了该点以外,定位资源与定位离键最近的节点的过程相似。
考虑到节点未必都在线的情况,资源的值被存在多个节点上(节点中的K个),并且,为了提供冗余,还有可能在更多的节点上储存值。储存值的节点将定期搜索网络中与储存值所对应的键接近的K个节点并且把值复制到这些节点上,这些节点可作为那些下线的节点的补充。另外还有缓存技术。
加入网络
- 新节点A必须知道某个引导节点B,并把它加入到自己相应的
K-桶
中。 - 生成一个随机的节点ID,直到离开网络,该节点会一直使用该ID号。
- 向B(A目前知道的唯一节点)发起一个查询请求(FIND_NODE),请求的ID是自己(就是查询自己)
- B收到该请求之后,会先把A的ID加入自己的相应的 K-桶中。并且根据 FIND_NODE 请求的约定,B会找到K个最接近 A 的节点,并返回给 A
- A收到这K个节点的ID之后,把他们加入自己的 K-桶
- 然后A会继续向刚刚拿到的这批节点(还未发送过请求的节点)发送查询请求(协议类型 FIND_NODE),如此往复,直至A建立了足够详细的路由表。
- 这种“自我定位”将使得
Kademlia
的其他节点(收到请求的节点)能够使用A的ID填充他们的K-桶,同时也能够使用那些查询过程的中间节点来填充A的K-桶。这已过程既让A获得了详细的路由表,也让其它节点知道了A节点的加入
在p2p网络中的应用
Kademlia可在文件分享网络中使用。由于没有中央服务器存储文件的索引,这部分工作就被平均地分配到所有的客户端中去:
假如一个节点希望分享某个文件,它先把文件的内容散列成一组数字,这组散列数字必须和节点ID有同样的长度。
然后,该节点便在网络中搜索ID值与文件的散列值相近的节点,并把它自己的IP地址存储在那些搜索到的节点上,也就是说,它把自己作为文件的源进行了发布。正在进行文件搜索的客户端将使用Kademlia
协议来寻找网络上ID值与希望寻找的文件的散列值最近的那个节点,然后取得存储在那个节点上的文件源列表。
由于一个键可以对应很多值,即同一个文件可以有多个源,每一个存储源列表的节点可能有不同的文件的源的信息,这样的话,源列表可以从与键值相近的K个节点获得。
文件名的搜索可以使用关键词来实现,文件名可以分割成连续的几个关键词,这些关键词都可以散列并且可以和相应的文件名和文件散列储存在网络中。搜索者可以使用其中的某个关键词,联系ID值与关键词散列最近的那个节点,取得包含该关键词的文件列表。由于在文件列表中的文件都有相关的散列值,通过该散列值就可利用上述通常取文件的方法获得要搜索的文件。
区块交换Block Exchanges
流行的文件共享系统 Bittorrent 能够成功地协调数百万节点之间的数据传输,这是通过依靠创新的数据交换协议完成的,但是,这只限于种子生态系统。
IPFS 实现了该协议的通用版本,称为 BitSwap,作为任意类型的数据市场来运营。该市场是 Filecoin 的基础,Filecoin 是构建于 IPFS 之上的 p2p 存储市场。
Merkle DAG(承担验证功能)
Merkle DAG 是 Merkle 树 和有向无环图(Directed Acyclic Graph,简称 DAG)的混合体。
Merkle 树确保在 p2p 网络上交换的数据块是正确的、没有受到损害的和未被修改的。这个验证是利用 加密哈希 函数数据块来完成的。
单独的数据块被称为“叶节点”,它们被哈希后,形成“非叶节点”。这些非叶节点然后能够组合在一起进行哈希,直到所有的数据块可以用单独一个根哈希值表示。如图所示。我们如果想验证文件是否正确,只需要验证根节点hash是否与本地计算的一致。
DAG 是一种无周期拓扑序列信息建模的方法。Merkle DAG 基本上是个数据结构,其中哈希被用来在 DAG 中引用数据块和对象。IPFS 上的所有内容能够被唯一地标识,因为每个数据块都有哈希值。此外,数据是防篡改的,因为数据的更改会改变哈希值。
版本控制系统
IPFS对数据对象使用类似的模型:只要与原始数据相对应的对象,并且可以访问任何新版本,则可以检索整个文件历史记录。鉴于数据块在整个网络上本地存储并且可以无限期地缓存,这意味着可以永久存储IPFS对象。
此外,IPFS 不依赖于对互联网协议的访问。数据可以分布在 覆盖网络 中,覆盖网络只是构建在另一个网络上的网络。这些功能值得注意,因为它们是抗审查网络的 核心要素。它可以成为促进自由言论以对抗全球互联网审查普及制度的工具,但是,我们也应该认识到不良行为者滥用言论的可能性。
自证明的文件系统(承担认证功能)
自我认证文件系统(SFS)。它是一个分布式文件系统,不需要特殊的数据交换权限。它是“自我认证”的,因为提供给客户端的数据是通过文件名(由服务器签名)进行身份验证的。您可以利用本地存储的透明性安全地访问远程内容。
IPFS基于这一概念创建了行星间名称空间(IPNS)。它是一种SFS,使用公钥密码学对网络用户发布的对象进行自我认证。我们前面提到,IPFS上的所有对象都可以唯一标识,但这也扩展到节点。网络上的每个节点都有一组公钥、私钥和一个节点ID,该节点ID是其公钥的散列。因此,节点可以使用其私钥对其发布的任何数据对象进行“签名”,并且可以使用发件人的公钥验证这些数据的真实性。
以下是对关键 IPFS 组成部分的快速回顾:
- 通过分布式哈希表,节点可以存储和共享数据,而无需中央协调
- IPNS 允许交换的数据立即进行预验证,并使用公钥密码进行验证。
- Merkle DAG 可实现唯一标识、防篡改和永久地存储的数据
IPFS的工作流程
IPFS add 命令
在 IPFS 系统中执行 add 操作就完成了上传操作,那是怎么上传的呢?
在 IPFS 文件存储系统中,每当上传一个新文件,系统会将单个文件拆分成若干个 256KB 的 block,每个 block 会有一个专属的 CID 进行标识,这个后面会详细讲;然后计算每一个 block 的 Hash 值,并存储再一个数组中,最后对这个数组求 Hash 得到文件的最终 Hash 值;接着将文件的 Hash 和所有的 blocks Hash 的数组组成成一个对象,也就形成了一种索引结构;最后把文件 block 和这个索引结构全部上传到 IPFS 节点,同步到 IPFS 网络。
文件上传时有两个值得注意的情况:1.文件特别小,如果文件小于 1KB 的话就不浪费一个 block 了,会直接和 Hash 一起上传到 IPFS。2.文件特别大,比如之前上传了一个 1G 的视频,之后又加了几 KB 的字幕文件,这种情况下未变化的 1G 部分是不会重新分配新的空间的,而只会为追加的字母文件部分分配新的 block,再重新上传 Hash。
因此,很好理解的是,即使是不同文件的相同部分也只会存储一份,很多文件的索引会指向同一个 block,所形成的结构就是 MerkleDAG 数据结构。
值得注意的是,当节点执行 add 操作时,会保留到本地 blockstore 中,但不会立刻主动上传到 IPFS 网络中,也就是说,与其连接的节点并不会存储这个文件,除非有某个节点请求过该 block 数据!因此,它并不是一个自动备份数据的分布式数据库。IPFS 这种设计是出于网络带宽、可靠性等方面的考虑。
add 执行逻辑如下图所示:
还有一个细节就是,当节点在执行add
命令时,还会广播自己的块信息,并维护一个所有发给这个节点的 block 请求列表,一旦 add 命令添加到数据满足这个列表,就会主动向对应的节点发送数据并且更新列表。
IPFS get 命令
那文件上传后,要怎么查找访问呢?
这就关系到上文所提到的 IPFS 索引结构是DHT
(分布式哈希表),通过对DHT
进行访问可以很快访问得到数据。
那如果想要查找一个本地没有的数据呢?
在 IPFS 系统中,所有和当前节点连接的节点会构成一个 swarm 网络,当节点发送一个文件请求(即get
)时,首先会在本地的 blockstore 里查找请求的数据,如果没找到的话,就会向 swarm 网络发出一个请求,通过网络中的DHT Routing
找到拥有该数据的节点。
怎么知道网络中哪个(哪些)节点拥有这个请求文件呢?
如上文add
命令所讲的那样,当一个节点加入到 IPFS 网络中后,会告诉其它节点自己存储了什么内容(通过广播DHT
),这样每当有用户希望检索的内容正好在这个节点上时,其它节点就会告诉用户要从这个节点索取他想要的内容。
一旦找到拥有这个数据的节点,就会把请求数据反馈回来,这样本地节点会把收到的 block 数据缓存一份到本地的 blockstore 中,这样整个网络中相当于多了一份原数据的拷贝,更多节点请求数据的话,查找就变得更容易,因此数据的不可丢失性也是基于这个原理,只要有一个节点保存着这个数据,就可以被全网获取。
在项目中,上传的文件可以通过
ipfs.io
网关直接获取到文件,类似于https://ipfs.io/ipfs/Qm.....
这样的网站地址,这个是什么原理呢?
ipfs.io
网关实际上就是一个 IPFS 节点,当我们打开上述这个网络链接的时候,实际上就是向这个节点发送了一次请求,因此ipfs.io
网关会帮我们去向拥有这个数据的节点请求这个 block(如果这个文件是自己刚在本地节点通过add
命令添加的话就会通过这种方式被上传到 IPFS 网络上),在swarm
网络中通过DHT Routing
获取到数据后,网关会自己先缓存一份,然后将数据通过 HTTP 协议发给我们,因此,就可以在浏览器直接看到这个文件啦!
而任何其他机器通过浏览器访问这个链接时,因为ipfs.io
网关已经缓存了这个文件,再次请求的时候,就不需要向原节点来请求数据了,可以直接从缓存中返回数据给浏览器。
内容标识符 CID(Content-ID)
现在考虑另一个问题,我们常见的图像为.jpg
、.png
,而常见的视频则是.mp4
一样,可以直接从后缀名判断文件类型。通过 IPFS 上传的文件也可以是多种类型,也包含了很多信息,怎么进行分辨呢?
IPFS 早期主要使用base58btc
对multihash
进行编码,但是在开发 IPLD(主要用来定义数据,给数据建模)的过程中会遇到很多与格式相关的问题,因此使用了一种叫CID
的文件寻址格式来对不同格式的数据进行管理,官方的定义为:
CID
是一种自描述式的内容寻址的识别符,必须使用加密散列函数来得到内容的地址
简单来说,CID
通过一些机制来对文件所包含的内容进行自描述,包含了版本信息、格式等。
CID 结构
目前CID
有v0
和v1
两种版本,v1
版本的CID
由V1Builder
生成
<cidv1> ::= <mb><version><mcp><mh>
# or, expanded:
<cidv1> ::= <multibase-prefix><cid-version><multicodec-packed-content-type><multihash-content-address>
如上面列举的代码所示,采用的机制叫multipleformats
,主要包括:multibase-prefix
表示CID
编码成字符串,cid-version
表示版本变量,multicodec-packed-content-type
表示内容的类型和格式(类似于后缀,但是作为标识符的一部分,支持的格式有限,且用户是不能随意修改的),multihash-content-address
表示哈希值(让CID
可以使用不同的 Hash 函数)。
目前CID
支持的multicodec-packed
编码有原生的protobuf
格式、IPLD CBOR
格式、git
、比特币和以太坊对象等格式,也在逐步开发支持更多格式。
CID
代码详解:
type Cid struct {str string}
type V0Builder struct {}
type V1Builder struct {}
Codec uint64
MhType uint64
MhLength int // Default: -1
Codec
表示内容的编码类型,如DagProtobuf
, DagCBOR
等,MhType
表示哈希算法,如SHA2_256
, SHA2_512
, SHA3_256
, SHA3_512
等,而MhLength
则表示生成哈希的长度。
而v0
版本的CID
由V0Builder
生成,以Qm
字符串开头,向后兼容,multibase
一直为base58btc
,multicodec
一直为protobuf-mdag
,cid-version
一直为cidv0
,multihash
表示为cidv0 ::= <multihash-content-address>
。
设计理念
通过CID
这种二进制的特性,大大提高了对于文件 Hash 的压缩效率,因此可以直接作为 URL 的一部分进行访问;通过multibase
的编码形式(如base58btc
)缩短了CID
的长度,这样更容易传输;可以表示任意格式、任何哈希函数的结果,十分灵活;可以通过结构中cid-version
参数进行编码版本的升级;不受限于历史内容。
IPNS
如上文所述,IPFS 中文件内容的改变会造成其哈希值的变化,在实际应用中,如果通过 IPFS 托管网站等需要版本更新迭代的应用,每一次都通过更新后的 Hash 访问很不方便,因此,需要一个映射方案以保证用户体验,这样用户在访问时仅需要访问一个固定地址。
IPNS(Inter-Planetary Naming System)
就提供了这样的服务,它提供了一个被私钥限定的哈希 ID(通常是 PeerID)来指向具体的 IPFS 文件,文件更新后会自动更新哈希 ID 的指向。
即使哈希值可以固定不变了,但是依然不便于记忆和输入,因此,有了更进一步的解决方案。
IPNS 同样兼容 DNS,可以使用DNS TXT
记录域名对应的 IPNS 哈希 ID,就可以域名来替换 IPNS 哈希 ID 来进行访问,从而实现更容易读写和记忆。
IPFS的演示
下载go-ipfs
将ipfs添加到环境变量
运行go-ipfs
# 初始化
ipfs init
# 运行
ipfs daemon
访问页面
http://127.0.0.1:5001/webui
网关
http://127.0.0.1:5001/8080
添加文件
ipfs add 111.txt
查看文件
使用hash值取查看文件
ipfs cat 复制的hash串
使用网关查看文件
http://localhost:8080/ipfs/复制的hash串
下载文件
ipfs cat /ipfs/[哈希值] > [要保存的文件名]
使用IPNS
先配置跨域
ipfs config --json API.HTTPHeaders.Access-Control-Allow-Origin '[\"https://gateway.ipfs.io\", \"http://localhost:3000\", \"http://127.0.0.1:5001\", \"https://webui.ipfs.io\"]'
ipfs config --json API.HTTPHeaders.Access-Control-Allow-Methods '[\"PUT\", \"POST\"]'
IPNS可以让你上传的文件始终保持一个hash地址
1.我们先上传一个文件夹
ipfs add -r E:\code\go-libp2p-DHT
2.IPNS发布
ipfs name publish /ipfs/QmVCU4Xz5thybj9sXZ9NiAAPQDkFFMM8sufbxcmVUCBz4E
3.查看IPNS
http://localhost:8080/ipns/k51qzi5uqu5dhnjk7rltjzdgycromfibatns8o7o709hxoeeqdwuqt0tdj351s
4.更新文件
新建一个new.txt文件,然后上传,再发布到IPNS
http://localhost:8080/ipns/k51qzi5uqu5dhnjk7rltjzdgycromfibatns8o7o709hxoeeqdwuqt0tdj351s
可有看到IPNS的地址并没有改变
5.使用公用网关访问
# 查询可以网关
https://luke.lol/ipfs.php
https://cf-ipfs.com/ipfs/:hash