目次

DAppsからIPFSにファイルをアップロードする

React で Web アプリケーションを実装し、HTML の FILE API で指定されたファイルを IPFS にアップロードする機能を実現します。

React プロジェクトの作成

> mkdir IPFS
> cd IPFS
IPFS> npx create-react-app ipfs-web --template typescript
IPFS> cd .\ipfs-web\
IPFS\ipfs-web> npm install --save ipfs-core ipfs-core-types util

ipfs-core」は JavaScript で IPFS を操作するためのライブラリ js-ipfs の一部です。

参考

IPFSUploader.tsx の実装

IPFS にファイルをアップロードする機能を提供する IPFSUploader.tsx コンポーネントを実装します。

IPFS\ipfs-web> mkdir src\components
IPFS\ipfs-web> code src\components\IPFSUploader.tsx
import React, { useState, useEffect } from 'react';
import type { IPFS } from 'ipfs-core-types';
import * as IPFSCore from 'ipfs-core';
 
let ipfs: IPFS;
 
function IPFSUploader() {
    const [isIpfsReady, setIpfsReady] = useState(Boolean(ipfs))
    const [version, setVersion] = useState("");
    const [cid, setCID] = useState("");
 
    useEffect(() => {
        startIpfs();
        return function cleanup () {
            if (ipfs && ipfs.stop) {
                console.log('Stopping IPFS');
                ipfs.stop().catch((err: any) => console.error(err));
                //ipfs = undefined;
                setIpfsReady(false);
            }
        }
    }, []);
 
    const startIpfs = async () => {
        if(ipfs){
            console.log('IPFS already started');
        /*
        } else if (window.ipfs && window.ipfs.enable) {
            console.log('Found window.ipfs')
            ipfs = await window.ipfs.enable({ commands: ['id'] })
        */
        } else {
            ipfs = await IPFSCore.create();
            console.log("IPFS Started");
            const _version = await ipfs.version();
            setVersion(_version.version);
        }
        setIpfsReady(Boolean(ipfs));
    };
    const fileSelected = async (event: React.ChangeEvent<HTMLInputElement>) => {
        if(!isIpfsReady) return;
 
        const targetFile = event.target.files?.[0];
        if(targetFile){
            const _result = await ipfs.add(targetFile);
            //const _result = await ipfs.add('Hello world');
            setCID(_result.path);
        }
    };
 
  return (
    <div>
        <h1>IPFS File Uploader</h1>
        <p>IPFS(ipfs-core) Version: { version }</p>
        <input id="input-file" type="file" onChange={fileSelected} disabled={!isIpfsReady}></input>
        <div>
            <p>Uploaded File CID: { cid }</p>
            <p><a href={`ipfs://${cid}`} target="_blank">open browser</a></p>
            <p><a href={`https://ipfs.io/ipfs/${cid}`} target="_blank">open ipfs.io public gateway</a></p>
        </div>
    </div>
  );
}
 
export default IPFSUploader;

App.tsx に IPFSUploader を追加

IPFS\ipfs-web> code src\App.tsx

不要な記述を削除し、IPFSUploader コンポーネントを追加します。

import React from 'react';
import './App.css';
import IPFSUploader from './components/IPFSUploader';
 
function App() {
  return (
    <div className="App">
      <header className="App-header">
        <IPFSUploader/>
      </header>
    </div>
  );
}
 
export default App;

実行

IPFS\ipfs-web> npm start

IPFSDirectoryUploader

MFS を利用してファイルをアップロードする。

IPFS\ipfs-web> code src\components\IPFSDirectoryUploader.tsx
import React, { useState, useEffect } from 'react';
import type { IPFS } from 'ipfs-core-types';
import * as IPFSCore from 'ipfs-core';
import { MFSEntry } from 'ipfs-core-types/dist/src/files/index';
 
let ipfs: IPFS;
 
function IPFSDirectoryUploader() {
    const [isIpfsReady, setIpfsReady] = useState(Boolean(ipfs))
    const [version, setVersion] = useState("");
    const [MFSEntlies, setMFSEntlies] = useState<MFSEntry[]>();
 
    useEffect(() => {
        startIpfs();
        return function cleanup () {
            if (ipfs && ipfs.stop) {
                console.log('Stopping IPFS');
                ipfs.stop().catch((err: any) => console.error(err));
                //ipfs = undefined;
                setIpfsReady(false);
            }
        }
    }, []);
 
    const startIpfs = async () => {
        if(ipfs){
            console.log('IPFS already started');
        /*
        } else if (window.ipfs && window.ipfs.enable) {
            console.log('Found window.ipfs')
            ipfs = await window.ipfs.enable({ commands: ['id'] })
        */
        } else {
            ipfs = await IPFSCore.create();
            console.log("IPFS Started");
            const _version = await ipfs.version();
            setVersion(_version.version);
 
            const lsResult = await ipfs.files.ls("/");
            let uploadDirectoryExist = false;
            for await (const file of lsResult){
                if(file.name === "upload-directory"){
                    console.log("/upload-directory is already exist.");
                    uploadDirectoryExist = true;
                }
            }
            if(!uploadDirectoryExist){
                console.log("mkdir /upload-directory");
                await ipfs.files.mkdir("/upload-directory");
            }
        }
        setIpfsReady(Boolean(ipfs));
    };
    const fileSelected = async (event: React.ChangeEvent<HTMLInputElement>) => {
        if(!isIpfsReady) return;
 
        const targetFile = event.target.files?.[0];
        if(targetFile){
            const fileReader = new window.FileReader();
            fileReader.readAsArrayBuffer(targetFile);
            fileReader.onloadend = async () => {
                let ab = fileReader.result as ArrayBuffer;
                const u8a = new Uint8Array(ab);
                console.log(`upload /upload-directory/${targetFile.name}`);
                await ipfs.files.write(`/upload-directory/${targetFile.name}`, u8a, {create: true});
                await ls("/upload-directory");
            };
        }
    };
    const ls = async (path: string) => {
        const lsResult = await ipfs.files.ls(path);
        for await (const file of lsResult){
            console.log(`${file.name}: ${file.cid.toString()}`);
        }
    };
    const MFSStat = async () => {
        if(!isIpfsReady) return;
 
        const lsResult = await ipfs.files.ls('/upload-directory');
        let _mfs_entlies: MFSEntry[] = [];
        for await (const file of lsResult){
            console.log(`${file.name}: ${file.cid.toString()}`);
            _mfs_entlies.push(file);
        }
        setMFSEntlies(_mfs_entlies);
    };
 
  return (
    <div>
        <h1>IPFS File Uploader</h1>
        <p>IPFS(ipfs-core) Version: { version }</p>
        <input id="input-file" type="file" onChange={fileSelected} disabled={!isIpfsReady}></input>
        <div>
            <p><button onClick={MFSStat}>MFS Stat</button></p>
            <p>
                { MFSEntlies?.map((file) => (
                    <div>
                        <p>{file.name}: {file.cid.toString()}</p>
                        <p>
                            <a href={`https://ipfs.io/ipfs/${file.cid.toString()}`} target="_blank" rel="noreferrer">Open</a>
                            <img src={`https://ipfs.io/ipfs/${file.cid.toString()}`} alt="IPFS img link."/>
                        </p>
                    </div>
                ))}
            </p>
        </div>
    </div>
  );
}
 
export default IPFSDirectoryUploader;

App.tsx に IPFSDirectoryUploader コンポーネントを記述する。