IPFSCDN.js - 基于 Helia 的浏览器端 CDN
一个纯 JavaScript 实现的浏览器端 CDN 系统,利用 IPFS/Helia 将访问者的浏览器变成内容分发节点。
核心概念
传统 CDN:用户从边缘节点下载内容 IPFSCDN:用户浏览器成为边缘节点,互相分享内容
传统模式: 用户 ← 服务器
IPFSCDN: 用户 ← 本地 IPFS 节点 ← 其他用户 / 服务器
功能特性
1. 自动图片拦截
- 页面加载时自动拦截所有
<img>标签 - 防止图片直接从源服务器加载
- 支持动态添加的图片(MutationObserver)
2. 本地 IPFS 存储
- 将图片下载并存入浏览器中的 Helia 节点
- 生成 CID(内容标识符)
- 自动固定内容,防止被垃圾回收
3. 本地内容提供
- 后续访问直接从本地节点获取图片
- 使用 Blob URL 显示图片
- 大幅降低源服务器负载
4. 图片状态标识 ⭐
每张图片右下角显示彩色圆点,直观展示当前状态:
| 颜色 | 状态 | 说明 |
|---|---|---|
| 🟢 绿色 | 本地提供 | 图片已从本地 IPFS 节点加载 |
| 🟡 黄色 | 远程回退 | 图片从原始服务器加载(下载失败或首次访问) |
| 🔵 蓝色(闪烁) | 处理中 | 正在下载并存储到 IPFS |
5. 自动内容分发
- 将 CID 广播到 IPFS 网络
- 其他节点可以发现并获取这些内容
- 定期重新广播,确保内容可发现性
6. 智能回退
- 下载失败时自动回退到原始源
- 支持重试机制
- 错误处理和恢复
使用方法
基本使用
只需在 HTML 页面中添加一行代码:
<script type="module" src="ipfscdn.js"></script>
IPFSCDN 会自动:
- 初始化 Helia 节点
- 拦截页面所有图片
- 下载并存储到本地 IPFS
- 从本地提供图片
- 广播 CID 到网络
- 在图片右下角显示状态标识
完整示例
<!DOCTYPE html>
<html>
<head>
<title>我的网站</title>
</head>
<body>
<!-- 普通图片 -->
<img src="https://example.com/image1.jpg" alt="图片1">
<img src="https://example.com/image2.jpg" alt="图片2">
<!-- 加载 IPFSCDN -->
<script type="module" src="ipfscdn.js"></script>
</body>
</html>
工作原理
1. 图片拦截流程
// 1. 保存原始 src
img.dataset.ipfscdnOriginalSrc = absoluteSrc
// 2. 设置占位符(防止立即加载)
img.src = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7'
// 3. 添加处理中标识
addBadgeToImage(img, 'loading')
// 4. 异步处理
processImage(img)
2. 存储流程
// 1. 下载图片
const response = await fetch(url)
const blob = await response.blob()
// 2. 存储到 IPFS
const cid = await fs.addBytes(uint8Array)
// 3. 固定内容
await helia.pins.add(cid)
// 4. 保存映射关系
localStorage.setItem('ipfscdn_' + hash(url), JSON.stringify({cid, timestamp}))
// 5. 更新标识为本地状态
addBadgeToImage(img, 'local')
3. 提供流程
// 1. 从本地获取
for await (const chunk of fs.cat(cid)) {
chunks.push(chunk)
}
// 2. 创建 Blob URL
const blob = new Blob(chunks)
const url = URL.createObjectURL(blob)
// 3. 设置图片源
img.src = url
// 4. 更新标识
addBadgeToImage(img, 'local')
4. 状态标识实现
// 创建 wrapper 容器
const wrapper = document.createElement('div')
wrapper.className = 'ipfscdn-wrapper'
wrapper.style.position = 'relative'
// 将图片放入 wrapper
img.parentNode.insertBefore(wrapper, img)
wrapper.appendChild(img)
// 添加状态标识
const badge = document.createElement('div')
badge.className = 'ipfscdn-badge local' // 或 'remote', 'loading'
badge.style.cssText = `
position: absolute;
bottom: 8px;
right: 8px;
width: 16px;
height: 16px;
border-radius: 50%;
border: 2px solid white;
z-index: 1000;
`
wrapper.appendChild(badge)
5. 分发流程
// 广播 CID 到 DHT
await helia.libp2p.contentRouting.provide(cid)
// 定期重新广播
setInterval(() => announceContent(), 60000)
配置选项
在 ipfscdn.js 中修改 CONFIG 对象:
const CONFIG = {
// 引导节点列表
BOOTSTRAP_NODES: [
'/ip4/164.92.116.163/udp/4001/webrtc-direct/...',
// ... 其他节点
],
// 并发处理数
CONCURRENT_LIMIT: 3,
// 重试次数
MAX_RETRIES: 3,
// 超时时间(毫秒)
TIMEOUT: 30000,
// 是否自动分发
AUTO_PROVIDE: true,
// 调试模式
DEBUG: true
}
技术栈
- IPFS 实现: Helia (浏览器端 IPFS 节点)
- 传输协议: WebRTC-direct, WebSockets
- 存储: Memory Blockstore / localStorage (映射缓存)
- 模块系统: ES Modules (通过 CDN 加载)
引导节点
默认配置了以下引导节点:
- 自定义节点(推荐)
``
/ip4/164.92.116.163/udp/4001/webrtc-direct/certhash/uEiB-LSkXRRznjYd7TGyssl9HjuwPrpduoQ-DKKVXIZK0jw/p2p/12D3KooWN57Fmw7NEAXiFa76TLH8JaJjUvTisfGu1ufjtjTt6Fg3
``
- 公共引导节点
``
/dnsaddr/bootstrap.libp2p.io/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN
/dnsaddr/bootstrap.libp2p.io/p2p/QmQCU2EcMqAqQPR2i9bChDtGNJchTbq5TbXJJ16u19uLTa
``
状态面板
IPFSCDN 会在页面右下角显示实时状态面板:
- 节点状态: Helia 节点连接状态
- 总图片数: 页面中发现的图片总数
- 本地缓存: 已存储到本地 IPFS 的图片数
- 本地提供: 从本地节点提供的图片数
- 远程回退: 从原始源加载的图片数
同时包含状态标识图例说明。
API 接口
IPFSCDN 暴露全局对象 window.ipfscdn,可用于调试:
// 查看统计
console.log(ipfscdn.stats)
// 手动触发内容广播
ipfscdn.announceContent()
// 查看缓存的 CID
console.log(ipfscdn.imageCache)
// 查看 Helia 节点
console.log(ipfscdn.helia)
// 为指定图片添加状态标识
ipfscdn.addBadgeToImage(imgElement, 'local')
浏览器兼容性
- Chrome 90+
- Firefox 88+
- Edge 90+
- Safari 14+
要求:
- 支持 ES Modules
- 支持 WebRTC
- 必须在 HTTPS 或 localhost 环境下运行
部署说明
1. 静态托管
IPFSCDN 是纯前端项目,可部署到任何静态托管:
# GitHub Pages
# 直接推送代码,在仓库设置中启用 Pages
# Netlify
# 拖拽上传文件夹即可
# 自有服务器
# 将文件复制到 web 根目录
2. HTTPS 要求
WebRTC 必须在安全上下文中运行,部署时必须使用 HTTPS。
本地开发:
# 使用 mkcert 生成本地证书
npx serve -s . --ssl-cert cert.pem --ssl-key key.pem
性能优化
1. 并发控制
限制同时处理的图片数量,避免网络拥塞:
CONCURRENT_LIMIT: 3 // 同时处理 3 张图片
2. 缓存策略
- 内存缓存:运行时快速访问
- localStorage:持久化 CID 映射
- IPFS 固定:防止内容被垃圾回收
3. 流式处理
大图片使用流式下载和存储,避免内存溢出:
for await (const chunk of fs.cat(cid)) {
chunks.push(chunk)
}
注意事项
1. CORS 限制
图片源服务器必须允许跨域访问,否则无法下载。
解决方案:
- 配置服务器 CORS 头
- 使用同域图片
- 使用图片代理服务
2. 存储空间
浏览器存储空间有限,大图片或大量图片可能超出限制。
建议:
- 定期清理未固定内容
- 监控存储使用情况
- 提供手动清理选项
3. 隐私考虑
IPFSCDN 会将图片内容广播到公共网络,不适合敏感图片。
未来改进
- [ ] 图片加密存储
- [ ] Service Worker 离线支持
- [ ] 图片压缩优化
- [ ] 更多内容类型支持(CSS, JS, 字体)
- [ ] 统计和监控面板
- [ ] 多节点负载均衡
相关项目
- Helia - IPFS 的 JavaScript 实现
- HeliaShare - 基于 Helia 的文件分享应用
许可证
MIT License
让浏览器成为 CDN 节点,共建去中心化网络!