web端上传文件夹到IPFS中的技术实现
1. html
首先web是不具备读取本地文件的,只能通过 来引导用户上来上传单个文件。通过 来实现文件夹上传。
<input type ="file" /> // 上传单个文件
<input type ="file" webkitdirectory /> 上传文件夹
2. ipfs
有看过go写的ipfs文件上传,通过命令行来实现。
ipfs add path -r . //path 文件名
想要在web通过用户上传文件夹到ipfs中,我首先看了js-ipfs库 发现实现不了,发现它是一个js实现的ipfs节点,我想要的是调用他的API来实现功能。后来发现同类型库 ipfs-http-client库(在客户端实现通过http调用ipfs接口)它提供了一个ipfs.add 方法。
import ipfsClient from 'ipfs-http-client'
const ipfs = ipfsClient.creat({
host:"localhost",
port :"5001",
protocol:"http"
}) // 连接本地的ipfs 后面可以连接远程的
连接本地的时候发现连接不了,仔细检查才知道原来是跨域问题😭,设置ipfs服务的请求头,让它允许跨域,然后重启ipfs。
ipfs config --json API.HTTPHeaders.Access-Control-Allow-Methods '["PUT", "GET", "POST", "OPTIONS","DELETE"]' // 用于跨域的方法
ipfs config --json API.HTTPHeaders.Access-Control-Allow-Origin '["*"]' // 允许哪些地址跨域 * 表示所有
ipfs daemon // 重启ipfs守护进程
上传文件夹的它的事件对象中是多个File对象,仔细查看ipfs.add方法后,它不能上传多个文件,它只能上传单个文件或者上传单个文件到文件夹中。
// data 只能是单文件,或者是一个对象 { path:file.name, content:"文件内容",mode:(文件的权限) string | number }
ipfs.add (data,{
progress: prog => console.log(`received: ${prog}`) // 将ipfs进程的内容 打印到当前进程中
}).then(res =>{
console.log(res.cid.toString()) // ipfs的哈希地址
})
最后查看ipfs的官网发现它有浏览器版的Desktop,将代码拉去下来看它是如何实现的(代码好复杂😯),它前端技术采用react组件的模式,依赖的ipfs-http-client 49.0版本,现在的版本已经52了。发现它是通过ipfs.addAll 方法来实现文件夹上传。最主要的问题是 让ipfs知道文件的层级关系。
import ipfsClient from 'ipfs-http-client'
import all from 'it-all'
const ipfs = ipfsClient.creat({
host:"localhost",
port :"5001",
protocol:"http"
}) // 连接本地的ipfs 后面可以连接远程的
const IGNORED_FILES = [".DS_Store", "thumbs.db", "desktop.ini"];
// 处理上传文件夹函数
const handleInput async (e){
const {files} = e.target
const filterFiles = normalizeFiles(files)
const souceFile = filesFIles
.filter($ => !IGNORED_FILES.includes($.path)))
.map($ => ($.path[0] === "/" ? { ...$, path: $.path.slice(1) } : $));
const result = await all(
ipfs.addAll(souceFile, {
pin: false,
wrapWithDirectory: false,
})
);
console.log(result) // 一个数组 [ {name:"assets/logo.png",hash:"afafafssshash",size :111}, ...
console.log(result[result.length - 1].cid.toString()); // 根路径的hash地址
// 通过本地的ipfs网关地址 http://localhost:8080/ipfs/hash地址 即可访问
]
}
function normalizeFiles(files) {
const streams = [];
for (const file of files) {
streams.push({
path: file.filepath || file.webkitRelativePath || file.name,
content: file,
size: file.size
});
}
return streams;
}
无论是ifs.add 或者ipfs.ipfs.addAll 方法,它们其实也是调用官方的ifs/v0/add api ,只是ipfs-http-client 库帮我们把做了一层封装而已。
最后总结可以通过 ipfs-http-client 的ipfs.addAll 来实现文件夹的上传。
\