web端上传文件夹到IPFS中的技术实现

3,057 阅读2分钟

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 来实现文件夹的上传。

\