IPFS技术(二)

一、IPFS API

IPFS提供了支持多种语言的 API版本,具体x可以在github上搜索ipfs查看相关介绍。这里我们主要介绍NodeJs上如何使用IPFS的API。

(1)准备环境
第一步:创建一个truffle-react项目;
第二步:在命令行上进入client目录,然后执行npm命令安装ipfs-api的包。

>> npm i --save ipfs-api

(2)使用IPFS API
第一步:在client目录下新建一个js文件,然后编辑文件,导入ipfs-api。

let IpfsApi = require("ipfs-api")

第二步:连接ipfs服务。

var ipfs = IpfsApi('localhost', '5001', {protocol : 'http'})

注意:连接ipfs服务之前,要先启动ipfs服务。

第三步:调用api方法

let ipfsTest = () => {
    return new Promise(async () => {
        // 把数据转换成Buffer
        const content = ipfs.types.Buffer.from('ABC')
        // 往ipfs中添加数据,添加成功后ipfs会返回一个hash
        const results = await ipfs.add(content)
        // 得到生成的hash
        const hash = results[0].hash
        console.log("往ipfs中添加内容成功,生成的hash为 : ", hash)
    })
}

ipfsTest()

二、常用方法介绍

下面以ipfs中的cat、ls方法为例介绍api的具体用法。详细介绍请参考:https://github.com/ipfs/js-ipfs#core-api。

函数 说明
ipfs.add(data, [options], [callback]) 添加数据或文件
ipfs.cat(ipfsPath, [options], [callback]) 查看指定ipfs哈希的文件
ipfs.ls(ipfsPath, [callback]) 列出指定ipfs哈希中的文件
let ipfsTest = () => {
    return new Promise(async () => {
        let info = await ipfs.cat(“QmXeKtRSz7SVKp8Qh6tXtv6whRF5WXWQPwsqA38houakhZ”)
        console.log("读取指定哈希位置存储的内容 : ", info.toString())

        // 列出指定哈希位置的所有文件
        let files= await ipfs.ls("QmSwkQt43JTUpXSAqT7z2DJF44HFz8i3juFGaLD3psKyVu")
        files.forEach((file) => {
            console.log(file.name)
        })
    })
}

三、IPFS与以太坊结合案例

功能需求:
1)定义一个图片上传页面;
2)将图片上传到ipfs,返回一个哈希值;
3)将哈希存储在以太坊(数据上链);
4)从以太坊获取哈希;
5)把指定哈希的图片展示在浏览器上;

该案例所涉及到的技术:react、web3、node.js和ipfs-api。

3.1 准备工作

(1)创建一个truffle-react工程,然后进入client目录,下载ipfs-api的包。
(2)关闭metamask插件。这样工会导致浏览器中没有了web3实例。因此,程序获取连接到本地网络(http://localhost:9545)。
IPFS技术(二)_第1张图片
(3)在命令行进入项目所在路径,执行truffle develop命令启动truffle内置服务。
IPFS技术(二)_第2张图片
(4)清理App.js文件

import React, { Component } from "react";
import SimpleStorageContract from "./contracts/SimpleStorage.json";
import getWeb3 from "./utils/getWeb3";

import "./App.css";

class App extends Component {
  state = { storageValue: 0, web3: null, accounts: null, contract: null };

  componentDidMount = async () => {
    try {
      const web3 = await getWeb3();
      const accounts = await web3.eth.getAccounts();
      const networkId = await web3.eth.net.getId();
      const deployedNetwork = SimpleStorageContract.networks[networkId];
      const instance = new web3.eth.Contract(
        SimpleStorageContract.abi,
        deployedNetwork && deployedNetwork.address,
      );
      this.setState({ web3, accounts, contract: instance });
    } catch (error) {
      console.error(error);
    }
  };

  render() {
    return (
        

Hello Truffle-React-IPFS

); } } export default App;

(5)启动React项目测试,在浏览器访问localhost:3000。

>> cd client
>> nmp start

3.2 上传图片到IPFS

(1)由于上传图片的哈希保存在合约中storedData字段中,但是该字段默认是uint类型,因此,需要修改为string类型。

pragma solidity ^0.5.0;

contract SimpleStorage {
  string storedData;

  function set(string x) public {
    storedData = x;
  }

  function get() public view returns (string) {
    return storedData;
  }
}

修改完成后,记得在truffle develop中重新编译和部署合约。

(2)在App.js文件中定义文件上传的页面元素。

render() {
        return (
            

请上传图片:

); }

运行效果如下图所示:
在这里插入图片描述
(3)定义一个按钮,点击按钮的时候抓取图片数据。


                    

Ref是React的一个特殊属性,用于绑定到render函数输出的任何组件上。当组件添加到DOM之后,ref属性会添加一个组件的引用到this.refs中。所以,可以通过this.refs来获取指定React组件。

界面效果如下图所示:
在这里插入图片描述
点击按钮,在控制台输出文件内容:
IPFS技术(二)_第3张图片
可以看到,已经抓取到文件的信息。那么接下来,我们就要文件上传到IPFS中。

(4)把图片上传到IPFS。
第一步:定义一个函数,实现上传功能。

upload = async(info) => {
	let reader = new FileReader()
    reader.readAsArrayBuffer(info)

    reader.onloadend = () => {
        console.log("result : ", reader.result)
    }
}

第二步:在按钮的onClick事件中调用该函数。


第三步:在App.js中引入ipfs。

// 导入ipfs
import IpfsApi from "ipfs-api";

// 创建ipfs实例(要启动ipfs daemon服务)
var ipfs = IpfsApi('localhost', '5001', {protocol : 'http'})

第四步:定义一个函数,该函数调用ipfs-api方法添加图片数据。

saveToIpfs = (data) => {
    return new Promise(async(resolve, reject) => {
        try {
            let res = await ipfs.add(data)
            let hash = res[0].hash
            resolve(hash)
        } catch (e) {
            reject(e)
        }
    })
 }

第五步:在upload函数中调用saveToIpfs函数。

upload = async (info) => {
    ...
    
    reader.onloadend = () => {
        this.saveToIpfs(reader.result).then(hash => {
            console.log("hash : ", hash)
        })
    }
}

运行程序,在页面上传文件失败。在浏览器console中看到以下错误信息:在这里插入图片描述
导致该问题的原因是由于未对CORS进行配置。所以先退出ipfs服务,然后运行下面命令:

>> ipfs config --json API.HTTPHeaders.Access-Control-Allow-Methods "[\"PUT\",\"GET\", \"POST\"]"
>> ipfs config --json API.HTTPHeaders.Access-Control-Allow-Origin "[\"*\"]"
>> ipfs config --json API.HTTPHeaders.Access-Control-Allow-Credentials "[\"true\"]"
>> ipfs config --json API.HTTPHeaders.Access-Control-Allow-Headers "[\"Authorization\"]"
>> ipfs config --json API.HTTPHeaders.Access-Control-Expose-Headers "[\"Location\"]"

运行完成后,重新启动ipfs服务即可。

第六步:把结果显示在页面上。

// 添加状态遍历picHash
state = {storageValue: 0, web3: null, accounts: null, contract: null, picHash: ''};

// 把上传图片的哈希保存在状态遍历中
upload = async (info) => {
    ...
    reader.onloadend = () => {
        this.saveToIpfs(reader.result).then(hash => {
            this.setState({picHash : hash})
        })
    }
}

// 显示哈希在页面上
render() {
    let {picHash} = this.state

    return (
        

请上传图片:

{ picHash &&

图片已经上传到IPFS:{picHash}

}
); }

运行程序,上传图片后的界面效果如下图所示:
IPFS技术(二)_第4张图片

3.3 把图片哈希上传到以太坊

第一步:定义状态变量isOk,记录是否上传成功

state = {storageValue: 0, web3: null, accounts: null, contract: null, picHash: '', isOk: false};

第二步:定义一个函数,调用合约set方法把哈希上传到以太坊。

saveToEth = async() => {
    try { 
    	// 获取合约实例和图片哈希
        let {contract, accounts, picHash} = this.state
        // 调用合约方法
        let res = await contract.methods.set(picHash).send({
            from : accounts[0]
        })
        this.setState({isOk : true})
    } catch (e) {
        console.log(e)
    }
}

第三步:添加按钮组件。

{
	picHash && 
}

测试程序,上传到以太坊后在控制台上可以到交易的信息:
在这里插入图片描述

3.4 显示上传图片

第一步:定义状态变量response,记录获取到的图片哈希

state = {storageValue: 0, web3: null, accounts: null, contract: null, picHash: '', isOk: false, response: ''};

第二步:先把isOk和response状态变量结构出来,然后再定义按钮组件。

render() {
	// 把isOk状态变量解构出来
	let {picHash, isOk, response} = this.state
	...
	
	return (
		...
		{
			isOk && 
		}
	);
}

第三步:实现getPicHash方法。

getPicHash = () => {
    try {
        // 获取合约中保存的图片哈希
        let {contract} = this.state
        let response = contract.methods.get().call()
        response.then(hash => {
            // 保存在状态变量response中
            this.setState({response : hash})
        })
    } catch (e) {
        console.log(e)
    }
}

第四步:显示图片。

{
	response && 
}

最后进行测试,效果如下图所示:

IPFS技术(二)_第5张图片

你可能感兴趣的:(技术总结和分享)