🖼️

React(Next.js) で画像のプレビュー・圧縮を試す

2020/12/06に公開

こんにちは!この記事は GameWith アドベントカレンダー6日目の記事です!

https://qiita.com/advent-calendar/2020/gamewith

React(Next.js) で画像のプレビューや、圧縮し Firebase Storage へのアップロードなどを試したので書いていこうと思います

画像のプレビュー

https://developer.mozilla.org/ja/docs/Web/API/URL/createObjectURL

よくある画像のプレビューは、createObjectURL を利用します

ファイルを丸ごと引数で渡して返ってきた URL をそのまま、imgsrc に渡せば画像が表示されます

export default function Create() {
  const [preview, setPreview] = useState('');

  const handleChangeFile = (e) => {
    const { files } = e.target;
    setPreview(window.URL.createObjectURL(files[0]));
  };

  return (
    <img src={preview} />
    <input
      type="file"
      name="photo"
      onChange={handleChangeFile}
    />
  )
}

ちなみに Next Image を利用すると、createObjectURLblob:http://localhost:3000/b2e0e749-3cda-4917-bee9-53a9f91ca59e のような形式を返し、Next Image 側がドメインをうまくパースできずエラーになり利用することはできませんでした

https://github.com/vercel/next.js/blob/master/errors/next-image-unconfigured-host.md

よく見る画像のプレビュー

<input type="file"> を非表示にしつつ、No Image を出していい感じ見せる場合はこんな感じになります

.preview {
  width: 300px;
  height: 300px;
}

.previewImg {
  object-fit: contain;
  width: 100%;
  height: 100%;
}

.inputPhoto {
  display: none;
}
import styles from './create.module.css';

export default function Create() {
  const [preview, setPreview] = useState('/img/no_image.png');

  const handleChangeFile = (e) => {
    const { files } = e.target;
    setPreview(window.URL.createObjectURL(files[0]));
  };

  return (
    <label htmlFor="photo">
      <div className={styles.preview}>
        <img src={preview} alt="preview" className={styles.previewImg} />
      </div>
      <input
        id="photo"
        type="file"
        name="photo"
        onChange={handleChangeFile}
        className={styles.inputPhoto}
      />
    </label>
  )
}

画像圧縮&Firebase Storage へのアップロード

https://github.com/blueimp/JavaScript-Load-Image

https://github.com/fengyuanchen/compressorjs

https://github.com/Donaldcwl/browser-image-compression

画像の圧縮はいろいろライブラリなどを調べたのですが、blueimp-load-image を採用しました

https://firebase.google.com/docs/storage/web/upload-files?hl=ja#upload_from_a_blob_or_file

https://developer.mozilla.org/ja/docs/Web/API/HTMLCanvasElement/toBlob

Firebase Storage は Blob のアップロードができるので、圧縮した画像を Canvas で受け取り、toBlobBlob に変換してアップロードします

import loadImage from 'blueimp-load-image';
import firebase from 'firebase/app';

export default function Create() {
 const handleChangeFile = async (e) => {
    const { files } = e.target;
    const canvas = await loadImage(files[0], {
      maxWidth: 1200,
      canvas: true,
    });
    canvas.image.toBlob((blob) => {
      firebase.storage().ref().child(`/${files[0].name}`).put(blob);
    }, files[0].type);
  };

  return (
      <input
        type="file"
        onChange={handleChangeFile}
      />
  )
}

圧縮は 1.6MB の画像が、520KB くらいになったので結構満足しています!

最後に

圧縮はいろいろ調べていたのですが、最終的にかなりスッキリとしたコードになりました!

ありがとうライブラリ🙏

Discussion