IPFSを使ったサンプルアプリケーションの作成手順を解説

IPFSを使ったサンプルアプリケーションの作成手順を解説

今回の内容

この記事では、IPFSを使ったサンプルアプリケーションを実装・解説します。

ターゲット

・ブロックチェーンを使ったストレージに興味がある方
・IPFSを実際に触ってみたい方
・IPFSを使ったアプリケーションを作成してみたい方

前提知識

・P2Pネットワーク、ブロックチェーンの基礎知識
・Ethereumの基礎知識
・Javascriptのソースが読める
・Solidityで書かれたコントラクトが読める

また今回までの記事をご覧になっていない方は、ぜひ下記に目を通しておいてください。

分散型ファイルストレージの概要
http://block-chain.jp/blockchain_study_salon_dojo/what-is-decentralized-distributed-storage/

IPFSとSwarmとは何か?
http://block-chain.jp/blockchain_study_salon_dojo/what-is-ipfs-and-swarm/

IPFSの環境構築の手順と基本的なコマンドを学ぶ
http://block-chain.jp/blockchain_study_salon_dojo/how-to-use-ipfs/

IPFSを使ったサンプルアプリケーションを作る

本章ではIPFSを使ったサンプルとして、下記のような動作をするWebアプリケーションを作成したいと思います。

  1. ブラウザからコンテンツをアップロード
  2. コンテンツをIPFSに保存
  3. コンテンツのメタ情報をEthereumのContractに保存

サンプルアプリケーションの完成イメージ(UI)

通常Webアプリケーションですと、以下のようなサーバーサイド構成を取ることが多いのですが、

今回作成するサンプルアプリケーションでは簡略化のためサーバーを挟まず、 クライアントサイドのJavaScript、HTMLだけでコンテンツのアップロード、情報管理を実現したいと思います。


[確認済み動作環境]
IPFS: version 0.4.14
Ethereum: geth version: 1.8.4-unstable
Browser: Google chrome version 65.0.3325.181
Solidity: 0.4.19
npm: 5.8.0
Web3: 0.20.6
Truffle: 4.0.6

事前準備

IPFS

JSからブラウザとは異なるドメインにアクセスしますので、IPFS daemon側で、クロスドメイン許可(CORS)を行います。

$ ipfs config Addresses.API /ip4/0.0.0.0/tcp/5001
$ ipfs config --json API.HTTPHeaders.Access-Control-Allow-Origin '["*"]'
$ ipfs config --json API.HTTPHeaders.Access-Control-Allow-Methods '["PUT", "GET", "POST"]'

Ethereum(geth)

gethもIPFSと同じくクロスドメイン許可(CORS)が必要になりますので、起動オプションに –rpccorsdomainを追加して起動してください。

 --rpccorsdomain "*" 

HTMLとBootstrapでUIを作成する

ファイルをアップロードするアプリケーションUIを作成します。
UIはHTMLのfile属性とform機能で実装します。

<form id="target">
    <input name="file" type="file">
    <input class="btn btn-primary" type="submit" name="submit" value=" アップロード">
</form>

次にHTML内で、下記のJSライブラリを3つ、CDN経由で呼び出します。

  • buffer: ファイルをアップロードした際にブラウザ上でバッファリングを行うためのライブラリ
  • web3: gethクライアントライブラリ
  • ipfs-api: JSからIPFS daemonにアクセスするためのjs-ipfsのbrowser版ライブラリ
<script src="https://wzrd.in/standalone/buffer"></script>
<script src="https://cdn.jsdelivr.net/npm/web3@0.20.6/dist/web3.min.js"></script>
<script src="https://unpkg.com/ipfs-api@9.0.0/dist/index.js"></script>

HTMLファイルの全体像は次のとおりです、index.htmlとして保存します。(※簡易的なデザインを行うために、bootstrapを利用しています。)

<!DOCTYPE html>
<html lang="ja">

<head>
 <meta charset="utf-8">
 <meta http-equiv="X-UA-Compatible" content="IE=edge">
 <meta name="viewport" content="width=device-width, initial-scale=1">
 <title>File upload by ipfs + ethereum</title>

 <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.1.0/css/bootstrap.min.css">
</head>

<body>
 <div class="container">
   <div>
     <div>
       <h2 class="text-center">File upload by ipfs + ethereum</h2>
       <hr/>
       <br/>
     </div>
   </div>

   <div class="row d-flex justify-content-center align-items-cente">
     <form id="target">
         <input name="file" type="file">
         <input class="btn btn-primary" type="submit" name="submit" value=" アップロード"> </div>
     </form>

     <br />
     <div class="alert alert-success" style="display: none" id="success-alert">
       <button type="button" class="close" data-dismiss="alert">x</button>
       <strong>Success! created IPFS URL </strong><div id="success-message"></div>
   </div>

     <table class="table" style="margin-top: 100px">
       <thead>
         <tr>
           <th scope="col">ファイル名</th>
           <th scope="col">ファイルURL</th>
         </tr>
       </thead>
       <tbody>

       </tbody>
     </table>
   </div>
 </div>

 <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
 <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.1.0/js/bootstrap.min.js"></script> 
 <script src="https://wzrd.in/standalone/buffer"></script>
 <script src="https://cdn.jsdelivr.net/npm/web3@0.20.6/dist/web3.min.js"></script>
 <script src="https://unpkg.com/ipfs-api@9.0.0/dist/index.js"></script>
 <script src="app.js"></script>
</body>
</html>    

Contractの作成

アップロードされたコンテンツのメタ情報をブロックチェーン(Contract)に保存するためにIpfs.solというContractを作成します。

コントラクトIpfsには以下のような役割の3つのメソッドだけを用意します。

  • setUploadFileInfo()・・・アップロードしたコンテンツの名前、ハッシュをFileInfoという配列に保存する
  • getUploadFileInfo()・・・FileInfo配列から添字をキーにして、保存した、コンテンツの名前、ハッシュを取得する
  • getLength()・・・FileInfo配列のサイズ(メタ情報の登録件数)取得
pragma solidity ^0.4.18;

contract Ipfs {

 struct FileInfo {
   string name;
   string fileHash;
 }

 FileInfo[] fileinfo;

 function setUploadFileInfo(string _name, string _hash) public {
   FileInfo memory meta = FileInfo(_name, _hash);
   fileinfo.push(meta);
 }

 function getUploadFileInfo(uint _id) public view returns(string, string) {
   return (fileinfo[_id].name, fileinfo[_id].fileHash);
 }

 function getLength() public view returns(uint) {
   return fileinfo.length;
 }
}

app.jsの作成

最後にContract、IPFSの操作を行うロジックapp.jsを作成します。
app.jsの全体は以下のようになります。

App = {
 ipfs: {},
 contract: {},
 web3: {},

 init: () => {
   App.initIpfs();
   App.initWeb3();
   App.initContract('0x02c4fbdef5be7d1e96a70ed3ad3dffbe6964175a');
   return App.bindEvents();
 },

 initIpfs: () => {
   App.ipfs = window.IpfsApi('localhost', '5001');   
 },

 initWeb3: () => {
   App.web3 = new Web3();
   App.web3.setProvider((new Web3.providers.HttpProvider('http://localhost:8545')));   
 },

 initContract: (address) => {
   $.getJSON('Ipfs.json', (aritifact) => {
     App.contract = App.web3.eth.contract(aritifact.abi).at(address);
     for (var i = 0; i < App.contract.getLength(); i++) {
       info = App.contract.getUploadFileInfo(i);
       App.setTemplate(info[0], info[1]);
     }
   });
 },
  bindEvents: () => {
   $('#target').submit(App.handleSubmit);
 },

 handleSubmit: (event) => {
   event.preventDefault();
   const file = event.target[0].files[0];
   const reader = new window.FileReader();
   reader.onloadend = () => App.saveIpfs(reader, file.name);
   reader.readAsArrayBuffer(file);
 },

 saveIpfs: (reader, filename) => {
   const buf = buffer.Buffer(reader.result)
   App.ipfs.files.add(buf, (err, result) => {
     if(err) {
       console.error(err);
       return;
     }
     const hash = result[0].hash;
     const url = `https://ipfs.io/ipfs/${hash}`;
     App.contract.setUploadFileInfo(filename, hash, { gas: 500000, from: App.web3.eth.coinbase });
     $('#success-alert').show();
     $('#success-message').text(url);
   })
 },

 setTemplate: (name, hash) => {
   var body = '';
   body += '<tr>';
   body += `<td>${name}</td>`   
   body += `<td>https://ipfs.io/ipfs/${hash}</td>`   
   body += '</tr>'
   $('tbody').prepend(body);
 }
};

$(() => {
 $(window).load(() => {
   App.init();
 });
});

IPFSホスティング

最後に、サンプルのソースもIPFSにアップロードして、IPFS上で動作させたいと思います。


記事の続きはオンラインサロンにて公開中!

IPFSカテゴリの最新記事