Unix File System (UnixFS)
Helia中定义一个UnixFS类用于文件处理。The Unix File System (UnixFS) is the data format used to represent files and all their links and metadata in IPFS.。UnixFS中的方法封装了常见的文件系统操作,使得在去中心化文件系统中处理文件和目录变得更加简单和直观。其主要操作由importer和exporter执行。
importer:导入文件并创建DAG
Importer从文件和目录构建有向无环图DAG,意味着文件被分解成块,然后使用“链接节点”以树状结构排列,连接在一起,给定文件的“散列”实际上是DAG中根节点的散列。
/**
* The importer creates UnixFS DAGs and stores the blocks that make
* them up in the passed blockstore.
*
* @example
*
* ```typescript
* import { importer } from 'ipfs-unixfs-importer'
* import { MemoryBlockstore } from 'blockstore-core'
*
* // store blocks in memory, other blockstores are available
* const blockstore = new MemoryBlockstore()
*
* const input = [{
* path: './foo.txt',
* content: Uint8Array.from([0, 1, 2, 3, 4])
* }, {
* path: './bar.txt',
* content: Uint8Array.from([0, 1, 2, 3, 4])
* }]
*
* for await (const entry of importer(input, blockstore)) {
* console.info(entry)
* // { cid: CID(), ... }
* }
* ```
*/
export async function* importer(source, blockstore, options = {}) {
let candidates;
if (Symbol.asyncIterator in source || Symbol.iterator in source) {
candidates = source;
}
else {
candidates = [source];
}
const wrapWithDirectory = options.wrapWithDirectory ?? false;
const shardSplitThresholdBytes = options.shardSplitThresholdBytes ?? 262144;
const shardFanoutBits = options.shardFanoutBits ?? 8;
const cidVersion = options.cidVersion ?? 1;
const rawLeaves = options.rawLeaves ?? true;
const leafType = options.leafType ?? 'file';
const fileImportConcurrency = options.fileImportConcurrency ?? 50;
const blockWriteConcurrency = options.blockWriteConcurrency ?? 10;
const reduceSingleLeafToSelf = options.reduceSingleLeafToSelf ?? true;
const chunker = options.chunker ?? fixedSize();
const chunkValidator = options.chunkValidator ?? defaultChunkValidator();
const buildDag = options.dagBuilder ?? defaultDagBuilder({
chunker,
chunkValidator,
wrapWithDirectory,
layout: options.layout ?? balanced(),
bufferImporter: options.bufferImporter ?? defaultBufferImporter({
cidVersion,
rawLeaves,
leafType,
onProgress: options.onProgress
}),
blockWriteConcurrency,
reduceSingleLeafToSelf,
cidVersion,
onProgress: options.onProgress
});
const buildTree = options.treeBuilder ?? defaultTreeBuilder({
wrapWithDirectory,
shardSplitThresholdBytes,
shardFanoutBits,
cidVersion,
onProgress: options.onProgress
});
for await (const entry of buildTree(parallelBatch(buildDag(candidates, blockstore), fileImportConcurrency), blockstore)) {
yield {
cid: entry.cid,
path: entry.path,
unixfs: entry.unixfs,
size: entry.size
};
}
}
构建树的函数buildTree会返回根节点的内容标识符 (CID)
exporter :导出 DAG
exporter 从 UnixFS 图中导出或读取文件数据,需要进行顺序遍历,逐一提取每个叶子节点中包含的数据。
/**
* Uses the given blockstore instance to fetch an IPFS node by a CID or path.
*
* Returns a {@link Promise} which resolves to a {@link UnixFSEntry}.
*
* @example
*
* ```typescript
* import { exporter } from 'ipfs-unixfs-exporter'
* import { CID } from 'multiformats/cid'
*
* const cid = CID.parse('QmFoo')
*
* const entry = await exporter(cid, blockstore, {
* signal: AbortSignal.timeout(50000)
* })
*
* if (entry.type === 'file') {
* for await (const chunk of entry.content()) {
* // chunk is a Uint8Array
* }
* }
* ```
*/
export async function exporter (path: string | CID, blockstore: ReadableStorage, options: ExporterOptions = {}): Promise<UnixFSEntry> {
const result = await last(walkPath(path, blockstore, options))
if (result == null) {
throw errCode(new Error(`Could not resolve ${path}`), 'ERR_NOT_FOUND')
}
return result
}
UnixFS相关函数
addAll(source: ImportCandidateStream, options?: Partial<AddOptions>): AsyncIterable<ImportResult>
功能: 从提供的流中导入多个文件和目录。用法: 你传入一个包含文件及其内容的流,返回一个异步可迭代对象,用于获取每个导入文件的结果。 addBytes(bytes: Uint8Array, options?: Partial<AddOptions>): Promise<CID>
功能: 将单个 Uint8Array
(二进制数据)作为文件添加到文件系统中。用法: 你提供一个 Uint8Array
的文件数据,返回一个 Promise,该 Promise 解析为新添加文件的 CID(内容标识符)。 addByteStream(bytes: ByteStream, options?: Partial<AddOptions>): Promise<CID>
功能: 将一系列 Uint8Array
数据流作为文件添加到文件系统中。用法: 你提供一个字节流(如文件的读取流),返回一个 Promise,该 Promise 解析为文件的 CID。 addFile(file: FileCandidate, options?: Partial<AddOptions>): Promise<CID>
功能: 添加一个文件,并可以附带可选的元数据(如路径、内容、权限和修改时间)。用法: 你提供一个包含文件元数据和内容的对象,返回一个 Promise,该 Promise 解析为文件的 CID。 addDirectory(dir?: Partial<DirectoryCandidate>, options?: Partial<AddOptions>): Promise<CID>
功能: 创建一个新目录。用法: 你可以选择性地传递目录的元数据,返回一个 Promise,该 Promise 解析为新目录的 CID。 cat(cid: CID, options?: Partial<CatOptions>): AsyncIterable<Uint8Array>
功能: 检索文件的内容。用法: 你提供一个 CID,返回一个异步可迭代对象,用于获取文件的数据。 chmod(cid: CID, mode: number, options?: Partial<ChmodOptions>): Promise<CID>
功能: 更改文件或目录的权限。用法: 你提供 CID 和新的权限模式,返回一个 Promise,该 Promise 解析为更新后的 CID。 cp(source: CID, target: CID, name: string, options?: Partial<CpOptions>): Promise<CID>
功能: 将文件或目录复制到目标目录中,并指定新名称。用法: 你提供源 CID、目标目录 CID 和新名称,返回一个 Promise,该 Promise 解析为更新后的目录 CID。 ls(cid: CID, options?: Partial<LsOptions>): AsyncIterable<UnixFSEntry>
功能: 列出目录的内容。用法: 你提供一个目录的 CID,返回一个异步可迭代对象,用于获取目录条目。 mkdir(cid: CID, dirname: string, options?: Partial<MkdirOptions>): Promise<CID>
功能: 在现有目录下创建一个新目录。用法: 你提供父目录的 CID 和新目录的名称,返回一个 Promise,该 Promise 解析为更新后的 CID。 rm(cid: CID, path: string, options?: Partial<RmOptions>): Promise<CID>
功能: 从现有目录中删除文件或目录。用法: 你提供目录 CID 和要删除的路径,返回一个 Promise,该 Promise 解析为更新后的 CID。 stat(cid: CID, options?: Partial<StatOptions>): Promise<UnixFSStats>
功能: 返回文件或目录的统计信息(如大小和权限)。用法: 你提供一个 CID,返回一个 Promise,该 Promise 解析为包含统计信息的对象。 touch(cid: CID, options?: Partial<TouchOptions>): Promise<CID>
功能: 更新文件或目录的修改时间。用法: 你提供一个 CID,返回一个 Promise,该 Promise 解析为更新后的 CID,包含新的修改时间。
代码
101-basics.js
// https://github.com/ipfs-examples/helia-examples/tree/main/examples/helia-101
/* eslint-disable no-console */
import { unixfs } from '@helia/unixfs'
import { createHelia } from 'helia'
const helia = await createHelia() // 顶层 await 创建一个 Helia 节点。这个节点是与分布式存储系统交互的基础。
const fs = unixfs(helia)// 创建文件系统
/* 存储文件 */
const encoder = new TextEncoder()// 用于将strings 编码为Uint8Arrays
const cid = await fs.addBytes(encoder.encode('Hello World 101'), { // add the bytes to your node and receive a unique content identifier
onProgress: (evt) => {
console.info('add event', evt.type, evt.detail)
}
})
console.log('Added file:', cid.toString())
/* 读取文件 */
const decoder = new TextDecoder()// this decoder will turn Uint8Arrays into strings
let text = ''
for await (const chunk of fs.cat(cid, {
onProgress: (evt) => {
console.info('cat event', evt.type, evt.detail)
}
})) {
text += decoder.decode(chunk, {
stream: true
})
}
console.log('Added file contents:', text)
运行输出
PS C:\Users\kingchuxing\Documents\IPFS\helia\helia-examples-main\examples\helia-101> npm run 101-basics
> helia-101@1.0.0 101-basics
> node 101-basics.js
add event unixfs:importer:progress:file:read { bytesRead: 15n, chunkSize: 15n, path: undefined }
add event blocks:put:providers:notify CID(bafkreife2klsil6kaxqhvmhgldpsvk5yutzm4i5bgjoq6fydefwtihnesa)
add event blocks:put:blockstore:put CID(bafkreife2klsil6kaxqhvmhgldpsvk5yutzm4i5bgjoq6fydefwtihnesa)
add event unixfs:importer:progress:file:write {
bytesWritten: 15n,
cid: CID(bafkreife2klsil6kaxqhvmhgldpsvk5yutzm4i5bgjoq6fydefwtihnesa),
path: undefined
}
add event unixfs:importer:progress:file:layout {
cid: CID(bafkreife2klsil6kaxqhvmhgldpsvk5yutzm4i5bgjoq6fydefwtihnesa),
path: undefined
}
Added file: bafkreife2klsil6kaxqhvmhgldpsvk5yutzm4i5bgjoq6fydefwtihnesa
cat event blocks:get:blockstore:get CID(bafkreife2klsil6kaxqhvmhgldpsvk5yutzm4i5bgjoq6fydefwtihnesa)
cat event unixfs:exporter:progress:raw { bytesRead: 15n, totalBytes: 15n, fileSize: 15n }
Added file contents: Hello World 101
(node:10028) MaxListenersExceededWarning: Possible EventTarget memory leak detected. 11 abort listeners added to [AbortSignal]. MaxListeners is 10.
Use events.setMaxListeners() to increase limit
(Use `node --trace-warnings ...` to show where the warning was created)