Reactを使用したIPFSへのデータアップロードアプリを作った

#要約
ReactとIPFSを使って、IPFSにデータを保存しそのハッシュ値を得るアプリを作った。
IPFSアップローダー:https://ipfsuploader-d6luh021e.now.sh/
ソースコード:https://github.com/andynuma/ipfs-uploader


#IPFS
Ethereumを用いたDappの開発で大きなデータをEthereumに保存しようとした場合に、Ethereumではデータの容量に制限があるため、大きなデータは保存できない。そこで、[IPFS]
(https://ipfs.io)が使用される場合がある。IPFSは簡単に言うとデータのハッシュ値を保存して、そのハッシュ値を指定すればデータが取得できるコンテンツ指向プロトコル。

#Reactでのipfs呼び出し
ipfs-apiを使用。
参考:https://github.com/ipfs/js-ipfs-http-client
##文字列アップロード
文字列をIPFSに送信する時のソースコードを以下に示す。

```tsx
import React, { FC, useState } from 'react';
import { Form, Button, Segment, Message } from 'semantic-ui-react';
import { getTextIpfsHash } from '../../utils/getIpfsHash';

const TextUpLoader: FC = () => {
  const [inputText, setText] = useState('');
  const [resultHash, setResultHash] = useState('');
  const [load, setLoad] = useState<boolean>(true);
  const [end, setEnd] = useState(false);

  const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
    event.preventDefault();
    setLoad(false);
    const res = await getTextIpfsHash(inputText);
    setResultHash(res);
    setEnd(true);
  };

  const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    setText(event.target.value);
  };

  return (
    <>
      <h1>Text Uploader</h1>
      <Segment.Group>
        <Segment>
          <Form onSubmit={handleSubmit}>
            <Form.Field>
              <input
                placeholder="Input data"
                value={inputText}
                onChange={handleChange}
              />
            </Form.Field>
            <Button type="submit">Submit</Button>
          </Form>
          {load ? <></> : <Message as="h3">Uploading...</Message>}
          {end ? <Message positive>End</Message> : <></>}
          <Segment>IPFS Hash : {resultHash}</Segment>
          <Segment>
            IPFS Link is{' '}
            <a
              href={`https://ipfs.io/ipfs/${resultHash}`}
              target="_blank"
              rel="noreferrer noopener"
            >
              here
            </a>
          </Segment>
        </Segment>
      </Segment.Group>
    </>
  );
};

export default TextUpLoader;

```
###動作例

<img width="1107" alt="スクリーンショット 2019-12-28 18.25.06.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189458/1817eaa0-c85e-5b41-19b9-4ae31016ee42.png">

IPFSのハッシュ値とそのデータへのURLが表示されている。

##ファイルアップロード
```tsx
import React, { useState } from 'react';
import { Segment, Form, Input, Button, Message } from 'semantic-ui-react';
import { getImageIpfsHash } from '../../utils/getIpfsHash';

const FileUpLoader = () => {
  const [buffer, setBuffer] = useState<ArrayBuffer>(new ArrayBuffer(0));
  const [resultHash, setResultHash] = useState('');
  const [load, setLoad] = useState<boolean>(true);
  const [end, setEnd] = useState(false);

  const handleChange = async (e: React.ChangeEvent<HTMLInputElement>) => {
    const files = e.target.files as FileList;
    const reader = new window.FileReader();
    reader.readAsArrayBuffer(files[0]);
    reader.onloadend = () => {
      const res = reader.result as string;
      setBuffer(Buffer.from(res));
      console.log(reader.result);
    };
    console.log(buffer);
  };

  const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();
    console.log('submitted...');
    setLoad(false);
    const hash: string = await getImageIpfsHash(buffer);
    setResultHash(hash);
    console.log(hash);
    setEnd(true);
  };

  return (
    <>
      <h1>File Uploader</h1>
      <Segment.Group>
        <Segment>
          <Form onSubmit={handleSubmit}>
            <Form.Field>
              <Input type="file" onChange={handleChange}></Input>
            </Form.Field>
            <Button>Submit</Button>
          </Form>
        </Segment>
        {load ? <></> : <Message as="h3">Uploading...</Message>}
        {end ? <Message positive>End</Message> : <></>}
        <Segment>IPFS Hash : {resultHash}</Segment>
        <Segment>
          IPFS Link is{' '}
          <a
            href={`https://ipfs.io/ipfs/${resultHash}`}
            target="_blank"
            rel="noreferrer noopener"
          >
            here
          </a>
        </Segment>
      </Segment.Group>
    </>
  );
};

export default FileUpLoader;

```

###動作例

<img width="1125" alt="スクリーンショット 2019-12-28 18.28.21.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189458/bd88edba-c0ec-f79b-1676-91ba3242cee7.png">

ファイルのhash値が表示されており、[リンク](https://ipfs.io/ipfs/QmQzmHevRSfrqeXurteFPcD2yMGDLyC1qvzsJwG6826ATt)をクリックするとスクリーンショットが表示される。

# Ethereumとの連携
IPFSを使用するのはDappでの使用が多い。ここではDappの開発で人気のEthereumにデータを保存する場合を考える。Ethereumには大きなデータを格納することはできないため、IPFSのハッシュ値を保存する。
これはかなり簡単でipfs-apiを使用してデータをIPFSに送信し保存、その後データのハッシュ値を取得してそのハッシュ値をコントラクトの関数の引数などに与えてデータを送信すれば良い。

コントラクトに`SetData`という関数があるとする。その関数の引数にipfsのハッシュ値を渡すだけである。以下にフォームハンドラを示す。

```jsx
const handleSubmit = async e => {
    e.preventDefault();
    setLoad(false);

    // ipfsに送信するためのcontent作成
    const content = ipfs.Buffer.from(e.target.value);

    // add
    const results = await ipfs.add(content);

    // hashを取得
    const hash = await results[0].hash;

    // ipfsのハッシュ値をチェーンに記録
    try {
      await setData(hash);
      setEnd(true);
    } catch (err) {
      setError(err.message);
    }

  };
```

#まとめ
IPFSにデータを送信してそのハッシュ値を得るアプリを作った（個人的にipfsを使用することがありハッシュ値の確認をしたかった）。