目录
Some things to try (Quick start)
IPFS安装:
$ tar xvfz go-ipfs.tar.gz
$ cd go-ipfs
$ ./install.sh
$ ipfs help
$ ipfs init
$ ipfs cat /ipfs/QmS4ustL54uo8FzR9455qaxZwuMiUhyvMcX9Ba8nUH4uVv/readme
$ ipfs cat /ipfs/<HASH>/quick-start
Some things to try (Quick start)
Basic proof of 'ipfs working' locally:
echo "hello world" > hello
ipfs add hello
# This should output a hash string that looks something like:
# QmT78zSuBmuS4z925WZfrqQ1qHaJ56DQaTfyMUF7F8ff5o
ipfs cat <that hash>
Add a file to ipfs:
$echo "hello world" >hello
$ipfs add hello
View it:
$ipfs cat <the-hash-you-got-here>
Try a directory:
$ mkdir foo
$ mkdir foo/bar
$ echo "baz" > foo/baz
$ echo "baz" > foo/bar/baz
$ ipfs add -r foo
View things:
ipfs ls <the-hash-here>
ipfs ls <the-hash-here>/bar
ipfs cat <the-hash-here>/baz
ipfs cat <the-hash-here>/bar/baz
ipfs cat <the-hash-here>/bar
ipfs ls <the-hash-here>/baz
Get:
ipfs get <the-hash-here> -o foo2
diff foo foo2
Objects:
ipfs object get <the-hash-here>
ipfs object get <the-hash-here>/foo2
ipfs object --help
Pin + GC:
ipfs pin add <the-hash-here>
ipfs repo gc
ipfs ls <the-hash-here>
ipfs pin rm <the-hash-here>
ipfs repo gc
Daemon:
ipfs daemon (in another terminal)
ipfs id
Network:
(must be online)
ipfs swarm peers // ipfs swarm peers| wc -l
ipfs id
ipfs cat <hash-of-remote-object>
Mount:
(warning: fuse is finicky!)
ipfs mount
cd /ipfs/<the-hash-here>
ls
Tool:
ipfs version
ipfs update
ipfs commands
ipfs config --help
open http://localhost:5001/webui
Browse:
webui:
http://localhost:5001/webui
Taking your Node Online
Once you’re ready to join your node to the public network, run the ipfs daemon in another terminal and wait for all three lines below to appear to know that your node is ready:
> ipfs daemon
Initializing daemon...
API server listening on /ip4/127.0.0.1/tcp/5001
Gateway server listening on /ip4/127.0.0.1/tcp/8080
Make note of the tcp ports you receive. If they are different, use yours in the commands below.
Transfer a file to IPFS
# On A
> ipfs add myfile.txt
added QmZJ1xT1T9KYkHhgRhbv8D7mYrbemaXwYUkg7CeHdrk1Ye myfile.txt
# On B
> ipfs get QmZJ1xT1T9KYkHhgRhbv8D7mYrbemaXwYUkg7CeHdrk1Ye
Saving file(s) to QmZJ1xT1T9KYkHhgRhbv8D7mYrbemaXwYUkg7CeHdrk1Ye
13 B / 13 B [=====================================================] 100.00% 1s
>ipfs ls -v QmZJ1xT1T9KYkHhgRhbv8D7mYrbemaXwYUkg7CeHdrk1Ye
>ipfs block stat: 查询block的数据大小,不包含子块。
>ipfs refs -r:列出所有数据块的子块信息
>ipfs ls or ipfs object links:显示所有的子块和块的大小
实验效果图:
ipfs cat /ipfs/QmW2WQi7j6c7UgJTarActp7tDNikE4B2qXtFCfLPdsgaTQ/cat.jpg >cat.jpg
QmRVWUfHFkEX8HGwtwau9pvcJ99vWySnQ9TryTQ6gqyJdB
-
IPFS与以太坊DApp结合的好处
在以太坊平台上,往区块链写入数据需要花费以太币,调用智能合约执行每一行代码的时候,都需要一定量的gas交易费。区块链存储大数据成本很高,而且不合理。
可以先将文件存储到IPFS,然后得到文件的Hash存储到以太坊区块链中。读取文件的时候,从以太坊区块链中获取文件的Hash,再通过Hash来读取IPFS网络上的文件。
使用官方提供的ipfs-api,可以很方便地在代码中操作节点将数据上传到IPFS,或者通过Hash从IPFS读取数据。
Truffle框架提供了快速搭建包含以太坊智能合约的React项目,可以通过"truffle unbox react"创建工程,然后安装ipfs-api依赖库。于是,可以在前端方便地调用IPFS的接口来读取、上传文件。truffle unbox react官网:
http://truffleframework.com/boxes/react
IPFS与DApp开发实战
7.1 安装Truffle unbox react
新建一个空目录"ipfs_dapp",然后执行安装命令
truffle unbox react
wenzildeiMac:ipfs_dapp wenzil$ pwd
/Users/wenzil/Desktop/study/ipfs_dapp
wenzildeiMac:ipfs_dapp wenzil$ truffle unbox react
Downloading...
Unpacking...
Setting up...
Unbox successful. Sweet!
Commands:
Compile: truffle compile
Migrate: truffle migrate
Test contracts: truffle test
Test dapp: npm test
Run dev server: npm run start
Build for production: npm run build
7.2 安装ipfs-api
在打开ipfs daemon联网之后,输入以下指令
$cd client su
#npm install --save ipfs-api 或者 npm install --save ipfs-http-client
7.3 修改智能合约代码
修改contracts目录中的"SimpleStorage.sol",修改后完整代码:
pragma solidity ^0.4.18;
contract SimpleStorage {
// 用于存储图片的哈希值
string storedData;
function set(string x) public {
storedData = x;
}
function get() public view returns (string) {
return storedData;
}
}
7.5 编译部署智能合约
ipfs-dapp$truffle compile
ipfs-dapp$ truffle migrate --reset
记住address
进入remix-ide,复制SimpleStorage.sol代码,然后切换到"Run"菜单,选择"Injected Web3"进行智能合约的部署
Remix-IDE部署智能合约
Remix-IDE部署智能合约-确认
然后复制智能合约地址,App.js文件中会用到合约地址为:"0xb177d6cf6916f128c9931e610c63208d9c5a40f3"
7.5 修改App.js文件
import React, {Component} from 'react'
import SimpleStorageContract from
'../build/contracts/SimpleStorage.json'
import getWeb3 from './utils/getWeb3'
import './css/oswald.css'
import './css/open-sans.css'
import './css/pure-min.css'
import './App.css'
const ipfsAPI = require('ipfs-api');
const ipfs = ipfsAPI({
host: 'localhost',
port: '5001',
protocol: 'http'
});
const contract = require('truffle-contract')
const simpleStorage = contract(SimpleStorageContract)
let account;
// Declaring this for later so we can chain functions on SimpleStorage.
let contractInstance;
let saveImageToIPFS = (reader) => {
return new Promise(function(resolve, reject) {
const buffer = Buffer.from(reader.result);
ipfs.add(buffer).then((response) => {
console.log(response)
resolve(response[0].hash);
}).catch((err) => {
console.error(err)
reject(err);
})
})
}
class App extends Component {
constructor(props) {
super(props)
this.state = {
blockChainHash: null,
web3: null,
address: null,
imageHash: null,
isSuccess: false
}
}
componentWillMount() {
ipfs.swarm.peers(function(err, res) {
if (err) {
console.error(err);
} else {
// var numPeers = res.Peers === null ? 0 : res.Peers.length;
// console.log("IPFS - connected to " + numPeers + " peers");
console.log(res);
}
});
getWeb3.then(results => {
this.setState({web3: results.web3})
// Instantiate contract once web3 provided.
this.instantiateContract()
}).catch(() => {
console.log('Error finding web3.')
})
}
instantiateContract = () => {
simpleStorage.setProvider(this.state.web3.currentProvider);
this.state.web3.eth.getAccounts((error, accounts) => {
account = accounts[0];
simpleStorage.at('0xb177d6cf6916f128c9931e610c63208d9c5a40f3').then((contract) => {
console.log(contract.address);
contractInstance = contract;
this.setState({address: contractInstance.address});
return;
});
})
}
render() {
return (
<div className="App">
<div style={{marginTop:10}}>智能合约地址:</div>
<div>{this.state.address}</div>
<div style={{marginTop:10}}>上传图片到IPFS:</div>
<div>
<label id="file">选择图片</label>
<input type="file" ref="file" id="file" name="file" multiple="multiple"/>
</div>
<button style={{marginTop:10}} onClick={() => {
var file = this.refs.file.files[0];
var reader = new FileReader();
reader.readAsArrayBuffer(file)
reader.onloadend = function(e) {
console.log(reader);
saveImageToIPFS(reader).then((hash) => {
console.log(hash);
this.setState({imageHash: hash})
});
}.bind(this);
}}>开始上传</button>
<div style={{marginTop:10}}>图片哈希值:{this.state.imageHash}</div>
<button onClick={() => {
contractInstance.set(this.state.imageHash, {from: account}).then(() => {
console.log('图片的hash已经写入到区块链!');
this.setState({isSuccess: true});
})
}}>图片哈希写入区块链</button>
{
this.state.isSuccess
? <div style={{marginTop:10}}>
<div>图片哈希成功写入区块链!</div>
<button onClick={() => {
contractInstance.get({from: account}).then((data) => {
console.log(data);
this.setState({blockChainHash: data});
})
}}>从区块链读取图片哈希</button>
</div>
: <div/>
}
{
this.state.blockChainHash
? <div style={{marginTop:10}}>
<div>从区块链读取到的哈希值:{this.state.blockChainHash}</div>
</div>
: <div/>
}
{
this.state.blockChainHash
? <div style={{marginTop:10}}>
<div>访问本地文件:</div>
<div>{"http://localhost:8082/ipfs/" + this.state.imageHash}</div>
<div>访问IPFS网关:</div>
<div>{"https://ipfs.io/ipfs/" + this.state.imageHash}</div>
<img alt="" style={{width: 100, height: 100 }} src={"https://ipfs.io/ipfs/" + this.state.imageHash}/>
</div>
: <img alt=""/>
}
</div>);
}
}
export default App
7.6 新建终端,执行"client$ npm run start"命令,
用react github 原始项目example跑的结果,看到这个图实属不易啊
小激动
但是我们看到stored value:0是错误的,这是因为部署合约的网络是localhost:8545; 而项目运行时injected web3来自MetaMask;
所以需要MetaMask连接到localhost:8545.
点击右上角猫猫,new network->url: http://127.0.0.1:8545 -> 可以查看/etc/hosts文件中有一行 127.0.0.1 localhost
MetaMask成功连接到本地网后,再运行:
npm run start
会自动打开网页,并且弹出MetaMask;
然后上传一张本地图片,之后点击"图片哈希写入区块链"按钮
运行结果1
最终效果如图:
运行结果
访问IPFS网关:
https://ipfs.io/ipfs/QmbHptfJfyuGAZxstFYgAVfz33cytR1seTD3ZabBSDd899
发现图片已经成功写入IPFS,如图:
运行结果3
备注:本地IPFS网关端口默认为8080,我的电脑上端口冲突了,系统自动修改为8082。
参考
1. https://docs.ipfs.io/introduction/usage/
2. IPFS简介和入门实践