本文分享一个基于 Helia 的浏览器端 IPFS 文件分享应用的完整开发经验,从架构设计到实现细节,希望能为想要构建去中心化 Web 应用的开发者提供参考。
HeliaShare 是一个纯前端实现的 IPFS 文件分享应用,用户可以在浏览器中直接上传文件到 IPFS 网络,通过 CID 获取文件,并主动分享本地数据块。整个应用无需后端服务器,完全运行在浏览器中。
项目地址: C:\GitHub\myblog\HeliaShare
核心功能:
Helia 是 IPFS 协议的现代化 JavaScript/TypeScript 实现,相比已弃用的 js-ipfs,具有以下优势:
前端: 纯 HTML + ES Modules (无框架)
IPFS: Helia (通过 CDN esm.sh 加载)
传输: WebRTC-direct, WebSockets
存储: Memory Blockstore / IndexedDB
为什么不使用 React/Vue?
考虑到这是一个技术演示项目,使用原生 JavaScript 可以更清晰地展示 Helia 的核心 API 使用方式,避免框架带来的认知负担。
┌─────────────────────────────────────────┐
│ 浏览器 (Browser) │
│ ┌─────────────────────────────────┐ │
│ │ HeliaShare 应用 │ │
│ │ ┌─────────┐ ┌───────────┐ │ │
│ │ │ UI 层 │◄──►│ 业务逻辑 │ │ │
│ │ └─────────┘ └─────┬─────┘ │ │
│ │ │ │ │
│ │ ┌─────────────────────▼─────┐ │ │
│ │ │ Helia IPFS 节点 │ │ │
│ │ │ ┌─────┐ ┌─────┐ ┌─────┐ │ │ │
│ │ │ │UnixFS│ │Block│ │DHT │ │ │ │
│ │ │ └─────┘ └─────┘ └─────┘ │ │ │
│ │ └───────────────────────────┘ │ │
│ └─────────────────────────────────┘ │
│ │ │
│ WebRTC / WebSocket │
│ │ │
└────────────────────┼────────────────────┘
│
┌───────────┼───────────┐
▼ ▼ ▼
┌────────┐ ┌────────┐ ┌────────┐
│引导节点 │ │IPFS网络│ │其他节点 │
└────────┘ └────────┘ └────────┘
1. 节点管理模块
// 初始化 Helia 节点
async function initHelia() {
helia = await createHelia({
libp2p: {
config: {
transport: { WebSockets: {}, WebRTC: {} }
}
}
})
// 初始化存储接口
fs = unixfs(helia)
stringStore = strings(helia)
}
2. 文件操作模块
// 上传文件
async function handleFileUpload(file) {
const arrayBuffer = await file.arrayBuffer()
const cid = await fs.addBytes(new Uint8Array(arrayBuffer))
await helia.pins.add(cid) // 自动固定
return cid
}
// 获取文件
async function fetchFile(cidString) {
const cid = CID.parse(cidString)
const chunks = []
for await (const chunk of fs.cat(cid)) {
chunks.push(chunk)
}
return new Blob(chunks)
}
3. 网络管理模块
// 连接默认引导节点
async function connectToBootstrapNodes() {
for (const nodeAddr of DEFAULT_BOOTSTRAP_NODES) {
await helia.libp2p.dial(nodeAddr)
}
}
决策: 使用纯 HTML + JavaScript,不依赖任何后端。
原因:
<script type="module" src="app.js"></script>
import { createHelia } from 'https://esm.sh/helia@^4.0.0'
决策: 内置默认引导节点,优先从该节点获取数据。
原因:
const DEFAULT_BOOTSTRAP_NODES = [
'/ip4/164.92.116.163/udp/4001/webrtc-direct/...'
]
// 获取文件时优先尝试引导节点
if (fromBootstrap) {
// 直接连接到引导节点获取
} else {
// 通过 DHT 查找其他节点
}
决策: 同时支持 CIDv0 (Qm开头) 和 CIDv1 (bafy开头)。
原因:
function getCidFormats(cid) {
// CIDv0 (Base58, Qm开头)
const cidv0 = CID.createV0(cid.multihash).toString(base58btc)
// CIDv1 Base32 (bafy开头)
const cidv1Base32 = cid.toString(base32)
return { cidv0, cidv1Base32, default: cid.toString() }
}
fs.addBytes() 生成 CIDhelia.pins.add() 防止被 GCCID.parse() 验证格式for await...of 处理大文件// 模拟进度更新
const progressInterval = setInterval(() => {
const currentProgress = 30 + Math.min(50, Math.floor(receivedBytes / 1024))
updateFetchProgress(currentProgress, '正在下载...',
`从${source}接收 ${formatBytes(receivedBytes)}`)
}, 800)
// 实际下载
for await (const chunk of fs.cat(cid)) {
chunks.push(chunk)
receivedBytes += chunk.length
}
问题: 浏览器 WebRTC 需要在 HTTPS 环境下运行。
解决: 在文档中明确说明部署必须使用 HTTPS,并提供本地测试方案。
问题: 浏览器内存有限,无法一次性加载大文件。
解决: 使用流式处理,分块读取和写入,避免内存溢出。
问题: 不同系统对 CID 格式的支持不一致。
解决: 同时显示多种格式,让用户自行选择。
HeliaShare 展示了如何在浏览器中构建一个完整的 IPFS 应用。通过合理的设计和 Helia 强大的功能,我们可以创建出既具有去中心化优势,又拥有良好用户体验的 Web 应用。
核心经验:
本文是 HeliaShare 系列文章的第一篇,后续将深入探讨具体实现细节。