基於IPFS去中心化相冊以太坊Dapp

Dapp-IPFS-Image
基於IPFS去中心化相冊以太坊Dapp

注意: 使用該 github 克隆到本地, 需要 執行 $ npm install 安裝依賴 node_modules

安裝IPFS
官網(訪問不了時,需要科學上網) https://ipfs.io
下載 解壓縮
$ tar xvfz go-ipfs_v0.4.10_darwin-amd64.tar.gz
$ cd go-ipfs
$ mv ipfs /usr/local/bin/ipfs

創建IPFS節點
$ ipfs init
$ cd ~/.ipfs

啓動服務器
ipfs daemon

跨域資源共享 CORS配置
control + c 終止ipfs服務器進程

$ ipfs config --json API.HTTPHeaders.Access-Control-Allow-Methods ‘[“PUT”, “GET”, “POST”, “OPTIONS”]’
$ 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服務器
$ ipfs daemon
新建終端執行:
$ ipfs cat /ipfs/QmYwAPJzv5CZsnA625s3Xf2nemtYgPpHdWEz79ojWnPbdG/readme
打開http://localhost:5001/webui會看到UI界面

需要 truffle ,node.js ,npm ,react 環境安裝 參考之前環境配置文章

項目開發
新建項目文件夾
$ mkdir 項目名IPFS-Image
cd IPFS-Image

下載unbox react 框架
$ truffle unbox react

安裝ipfs-api
$ npm install ipfs-api --save-dev

驗證框架和環境:
編譯合約
$ truffle compile

開啓前端
$ npm start

看見 項目頁面, ok

修改項目框架代碼:
Atom編輯器打開項目:
$ atom ./

編寫智能合約
修改contracts/SimpleStorage.sol文件

pragma solidity ^0.4.19;

contract SimpleStorage {

string[] public photoArr;  //定義一個 裝圖片hash值的字符串數組

function storePhoto(string hash) public {  //定義添加新圖片hash方法
    photoArr.push(hash);   //往字符串數組末尾 添加 新圖片的 hash
}

function getPhoto(uint index) public view returns (uint, string){  //定義函數:傳入圖片序列號,獲取圖片hash數組長度 和 對應序列號的 裝圖片hash值的字符串數組
    return (photoArr.length, photoArr[index]);
}

}
修改 src/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’

import ipfsAPI from ‘ipfs-api’
const ipfs = ipfsAPI({host: ‘localhost’, port: ‘5001’, protocal: ‘http’})

const contractAddress = “0xd7d25cd0f1ab028a43576b93380c52992716a0d1”
let simpleStorageInstance

let saveImageOnIpfs = (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 = {
		photos: [],
		count: 0,
		web3: null
	}
}

componentWillMount() {
// Get network provider and web3 instance. See utils/getWeb3 for more info.

getWeb3.then(results => {
	this.setState({web3: results.web3})
		this.instantiateContract()
	}).catch(() => {
		console.log('Error finding web3.')
	})
}

instantiateContract() {
	const that = this
	const contract = require('truffle-contract')
	const simpleStorage = contract(SimpleStorageContract)
	simpleStorage.setProvider(this.state.web3.currentProvider)

	this.state.web3.eth.getAccounts((error, accounts) => {
    	simpleStorage.at(contractAddress).then((instance) => {
			simpleStorageInstance = instance
		})
		.then(result => {
			return simpleStorageInstance.getPhoto(0)
		})
		.then(result => {
			console.log(result)
			let imgNum = result[0].c[0]
			if(imgNum===0){
				return
			}
			if(imgNum===1){
				this.setState({
					count: imgNum,
					photos: this.state.photos.concat([result[1]])
				})
			}
			if(imgNum>1){
				for(let i=0;i<imgNum;i++){
					(function(i){
						simpleStorageInstance.getPhoto(i)
						.then(result => {
							that.setState({
								photos: that.state.photos.concat([result[1]])
							})
						})
					})(i)
				}	
			}
		})
	})
}

render() {
	let doms = [],
		photos = this.state.photos
	for(let i=0; i<photos.length;i++){
		doms.push(<div key={i}><img src={"http://localhost:8080/ipfs/" + photos[i]}/></div>)
	}
	
	return (
		<div className="App">
			<header>上傳圖片至ipfs,並保存信息至以太坊區塊</header>
			<div className="upload-container">
				<label id="file">選擇圖片</label>
				<input type="file" ref="file" id="file" name="file" multiple="multiple" onChange={e => this.change(e)}/>
				<button onClick={() => this.upload()}>上傳</button>
			</div>
			<div className="img-container">
				{doms}
			</div>
		</div>
	);
}

upload() {
	var file = this.refs.file.files[0];
	console.log(file)
	var reader = new FileReader();
	// reader.readAsDataURL(file);
	reader.readAsArrayBuffer(file)
	reader.onloadend = (e) => {
		//console.log(reader);
		saveImageOnIpfs(reader).then((hash) => {
			console.log(hash);
			this.setState({imgSrc: hash})
				simpleStorageInstance.storePhoto(hash, {from: this.state.web3.eth.accounts[0]})
				.then(() => {
					console.log("寫入區塊成功")
					this.setState({
						photos: this.state.photos.concat([hash])
					})
				})
		});
	}
}
change(e){
	console.log(e.target.value)
}  

}

export default App
修改src/app.css 前端 UI CSS
/* PAGE */

  • {
    margin: 0;
    padding: 0;
    }
    html, body, #root, .App{
    width: 100%;
    height: 100%;
    }
    .App{
    display: flex;
    flex-direction: column;
    }
    header{
    height: 60px;
    line-height: 60px;
    padding: 0 20px;
    background: #cfd8dc;
    border-bottom: 1px solid #eee;
    font-size: 24px;
    color: #fff;
    }
    .upload-container {
    height: 100px;
    display: flex;
    justify-content: center;
    align-items: center;
    background: #cfd8dc;
    color: #666;
    }
    .img-container {
    flex: 1;
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    /* align-items: center; */
    height: 80px;
    background: #eceff1;
    }
    .img-container div{
    width: 200px;
    height: 200px;
    margin: 20px;
    box-shadow: 0 0 10px #ccc;
    border-radius: 3px;
    }
    .img-container div{
    display: flex;
    justify-content: center;
    align-items: center;
    }
    .img-container div img{
    max-width: 200px;
    max-height: 200px;
    }
    .upload-container input{
    font-size: 14px;
    display: flex;
    align-items: center;
    margin-left: 10px;
    }
    .upload-container button{
    height: 30px;
    width: 80px;
    line-height: 30px;
    font-size: 14px;
    color: #fff;
    border: 0;
    outline: none;
    cursor: pointer;
    border-radius: 3px;
    background: cadetblue
    }
    .upload-container button:hover{
    opacity: 0.7;
    }
    本地測試:
    編譯部署合約

$ truffle develop //啓動truffle本地合約測試框架

(Mnemonic:後面 就是本地 以太坊測試框架區塊鏈 錢包的助記詞,可用於直接導入 Metamask錢包中)

$ truffle(develop)> compile //編譯
$ truffle(develop)> migrate //部署合約

複製智能合約 SimpleStorage 的地址

修改 src/app.js 合約地址
const contractAddress = “0x345ca3e014aaf5dca488057592ee47305d9b3e10” //SimpleStorage合約地址

新建終端(truffle develop框架不要關), 啓動IPFS服務器:
$ ipfs daemon

新建終端, 啓動 前端:
$ npm start

安裝 Metamask 錢包(科學上網):
https://metamask.io

設置RPC
添加http://localhost:9545

調到RPC localhost 9545網絡

導入 truffl 框架 分配的私鑰

選擇圖片文件上傳. 提示消耗Gas 點擊SUBMIT

如果消耗gas出錯, 可以導入truffle 助記詞到MetaMask錢包,重試.

圖片顯示項目成功

可以多上傳幾張測試

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章