简介
星际文件系统(InterPlanetary File System,缩写为IPFS)是一个旨在实现文件的分布式存储、共享和持久化的网络传输协议。它是一种内容可寻址的对等超媒体分发协议。在 IPFS 网络中的节点构成一个分布式文件系统。
主要有以下几个特点
- 支持弹性网络:在传统的互联网中,文件是存储在中心化服务器上的,假如你的文件服务器宕机了 。就会导致文件不可访问。但在
IPFS
中,你还可以从其他节点获取文件。 - 内容审核更困难:因为
IPFS
上的文件可以来自很多地方,所以任何人都更难封锁。 - 更快的文件获取速度:由于你可以从附近的节点上获取文件。而不是其他更远的地方,所以大大提升了访问速度。(类似于 CDN)
今天的万维网是以所有权和访问权为结构的,这意味着你要想从拥有者那里获得文件,必须要先获得访问权。但IPFS
是基于占有和参与的理念的,即人们拥有着彼此的文件并参与提供文件。
IPFS
只有在人们积极参与的情况下才能更好的运转。如果你用你的电脑使用IPFS
共享文件,但后来你关闭了,其他人就不能再从你那里获得文件。但是,如果文件的副本存储在其他运行着IPFS
的计算机上,则这些文件就可以做出共享。
工作原理
IPFS 遵循了三个基本原则
-
通过内容寻址来进行唯一的标识
一般情况下文件的路径是基于位置,无论是你本地的文件
/Users/..../code.js
还是网络上可访问的文件http://www.google.com/index.html
。但
IPFS
的文件路径不是基于地址的,而是基于文件内容。IPFS
会基于文件内容生成一个加密的哈希值,这个值用来作为路径的一部分,如下面地址中ipfs/
后面的内容/ipfs/QmcuLr8xuHm6ViSYuppjDxCXE8vDuUBKmdAwcrTJRRnEUY
IPFS
使用内容寻址,即通过内容而不是通过它的位置来定位文件。每个使用IPFS
协议的文件都有一个内容标识符,即CID
,也就是它的哈希值。这个哈希值对于文件来说是独一无二的。 -
通过有向无环图(DAG)进行内容链接
IPFS
和许多其他分布式系统一样使用了一种称为有向无环图(DAG)的数据结构。具体来说,是使用了默克尔有向无环图(Merkle DAG
)。为了建立一个
Merkle DAG
表示你的文件,IPFS
通常首先将文件分块。(分块意味着文件的不同部分可以有不同的来源,并被快速验证)。分块的内容会经过哈希运算得到一个哈希值作为CID
,并作为Merkle DAG
的节点。 这些节点再次经过哈希运算也就得到了该文件的CID
。同样,如果文件处于文件夹内,则也会根据文件夹内的文件内容经过哈希运算得到文件夹的
CID
。如果你有两个类似的文件,文件分块后的生成的
CID
如果有相同的,则可 以引用相同的数据子集,可以很好的达到去重的效果。例如,更新文件时,更新的文件和未更新文件的第一个块的
CID
相同,则可以引用相同的块。而不是重新创建。因此,概括地说,
IPFS
让你为内容赋予CID
,并将这些内容连接在Merkle DAG
中。 -
通过分布式哈希表(
DHT
) 发现内容DHT
的最主要思想是全网维护一个巨大的文件索引哈希表,这个哈希表的条目形如<Key,Value>
。其中,Key
通常是文件的某个哈希算法下的哈希值(也可以是文件名或者文件内容描述) ,而Value
则是存储文件的 IP 地址。查询时,仅需要提供Key
,就能从表中查询到存储节点的地址。由于这个哈希表很大,因此它会被分割成小块,按照一定的算法和规则分布到全网各个节点上。 每个节点仅需要维护一小块哈希表,但每块哈希表不止由一个节点维护(这样即使节点意外挂掉也有其他的节点使
DHT
可用)当节点接受到查询请求后,如果可以在自己的桶(每一个节点维护的
DHT
子集被称为”桶“)中找到则回复,否则联系最近节点回复。这个过程一直到找到目标节点为止。
这三个原则相互依存,实现 IPFS 的生态系统。
使用
命令行使用
命令行安装 js-ipfs
$ npm install -g ipfs
也可以安装客户端 https://ipfs.tech/#install
初始化ipfs
仓库
$ jsipfs init
initializing ipfs node at .jsipfs
启动本地 ipfs
网络实例
$ jsipfs daemon
Initializing IPFS daemon...
HTTP API listening on /ip4/127.0.0.1/tcp/5002/http
gRPC listening on /ip4/127.0.0.1/tcp/5003/ws
Gateway (read only) listening on /ip4/127.0.0.1/tcp/9090/http
Web UI available at http://127.0.0.1:5002/webui
Daemon is ready
添加文件
$ jsipfs add util.ts
added QmbFMke1KXqnYyBBWxB74N4c5SBnJMVAiMNRcGu6x1AwQH util.ts
访问文件内容
$ jsipfs cat QmbFMke1KXqnYyBBWxB74N4c5SBnJMVAiMNRcGu6x1AwQH
API 使用
首先需要一个可以公网访问的服务器作为 ipfs
节点。可以选择自己的公网服务器或者 infura 服务。这里就以 infura
为例,而且其还有着 5G 的免费空间可以使用。在 infura
上创建 ipfs
项目后会有一个 projectId
和 projectSecret
安装
项目中安装 ipfs-http-client
$ yarn add ipfs-http-client
初始化
import { create, urlSource, CID } from 'ipfs-http-client'
import fs from 'node:fs'
const projectId = 'xxxxxx'
const projectSecret = 'xxxxxx'
const auth =
'Basic ' + Buffer.from(projectId + ':' + projectSecret).toString('base64')
const ipfs = create({
host: 'ipfs.infura.io',
port: 5001,
protocol: 'https',
headers: {
authorization: auth
}
})
上传资源
const upload = async () => {
// 上传本地文件 或 网络资源 或 文本
// 1. 本地资源
const file = fs.readFileSync('./test.sol', 'utf-8')
// 2. 网络资源
//const file = urlSource('https://www.baidu.com/img/PCfb_5bf082d29588c07f842ccde3f97243ea.png')
// 3. 文本
// const file = 'hello world'
const result = await ipfs.add(file)
console.log(result)
}
upload()
上传成功后会返回如下格式的内容
{
path: 'QmNvHxnJQ6Ho9LWw1FwCn3nqcJXSeM76cmmd6JvoLEzW8Q',
cid: CID(QmNvHxnJQ6Ho9LWw1FwCn3nqcJXSeM76cmmd6JvoLEzW8Q),
size: 593
}
通过浏览器的地址 https://ipfs.infura.io/ipfs/${path}
,即可访问内容
读取资源
const cat = async () => {
const decoder = new TextDecoder()
let content = ''
for await (const chunk of ipfs.cat(
'QmNvHxnJQ6Ho9LWw1FwCn3nqcJXSeM76cmmd6JvoLEzW8Q'
)) {
content += decoder.decode(chunk)
}
console.log(content)
}