LoginSignup
1
3

More than 1 year has passed since last update.

Firebaseを使わずにブラウザ上でローカル画像をリサイズ【canvasのdrawImage()をReactで利用した方法】

Posted at

Firebase側で画像のサイズを変えることはできますがFirebase側への通信量を抑えたいと思ったのでブラウザ上でサイズを小さくすることにしたことのまとめです。
プロフィール画像を想定していて画像の最大幅がある程度は分かっていた流れがあります。

成果物

スクリーンショット 2021-05-14 17.56.54.png
アップロードされた画像は canvas の drawImage() でリサイズされたものです。

ソースコード

App.js
import { useState } from "react";
import "./styles.css";

const uploadToFirestore = () => {
  console.log("画像をuploadしました"); //ダミー処理
};

const App = () => {
  const [uploadImg, setUploadImg] = useState("");

  const handleUploadImg = (e) => {
    if (!e.currentTarget.files || e.currentTarget.files.length === 0) return;

    const fileReader = new FileReader();
    const file = e.currentTarget.files[0];
    if (file.type !== "image/jpeg" && file.type !== "image/png") return;

    fileReader.onloadend = () => {
      const fileImage = new Image();
      fileImage.onload = () => {
        const uploadWidth = fileImage.width,
          uploadHeight = fileImage.height;

        const wantWidth = uploadWidth < 200 ? uploadWidth : 200,
          resizeRatio = uploadWidth / wantWidth,
          wantHeight = uploadHeight / resizeRatio;

        const canvas = document.createElement("canvas");
        canvas.width = wantWidth;
        canvas.height = wantHeight;
        let ctx = canvas.getContext("2d");
        ctx.drawImage(fileImage, 0, 0, wantWidth, wantHeight);
        ctx.canvas.toBlob(
          async (blob) => {
            const resizedImageFile = new File([blob], file.name, {
              type: file.type,
              lastModified: Date.now()
            });
            const blobToUrl = URL.createObjectURL(blob); //blobをimgのsrc属性で使える形へ変換
            setUploadImg(blobToUrl); //リサイズした画像を表示
            uploadToFirestore(resizedImageFile); //resizedImageFileをFirestoreへ
          },
          file.type,
          1
        );
      };
      fileImage.src = fileReader.result;
    };
    fileReader.readAsDataURL(file);
  };

  return (
    <div className="App">
      <h1>React画像アップロード</h1>
      <input type="file" onChange={(e) => handleUploadImg(e)} />
      <div style={{ margin: "10px" }}>
        <img src={uploadImg} alt="アップロード画像" />
      </div>
    </div>
  );
};

export default App;

codesandbox URL: https://codesandbox.io/s/react-img-size-change-canvas-n1iln?file=/src/App.js:0-1967

コード解説 概要

・FileReaderインスタンスでアップロードした画像を取り出す
・Imageインスタンスを呼び出しcanvasメソッドを使ってリサイズする
・fileImage.onloadの中身のコードについて

FileReaderインスタンスでアップロードした画像を取り出す

App.js
// 省略

const fileReader = new FileReader();
const file = e.currentTarget.files[0];

// 省略

fileReader.readAsDataURL(file);

FileReaderインスタンスであるfileReaderの中のreadAsDataURL(file)を読むことでfileReder.onloadを発火させます。

ここの詳細については前回の記事、Reactで画像アップロード機能を実装してローカルファイルを読み込ませるで書いています。

Imageインスタンスを呼び出しcanvasメソッドを使ってリサイズする

App.js
// 省略

 const fileImage = new Image();
      fileImage.onload = () => {
      // 省略
  }
  fileImage.src = fileReader.result;

// 省略
}

ImageインスタンスであるfileImageのsrcに画像データ(blobデータ)を代入するとonloadが走ります。
ちなみにfileReader.resultの中身を imgタグ の src属性 に入れても読み込むことができます。

fileImage.onloadの中身のコードについて

App.js
// 省略
      fileImage.onload = () => {
        //読み込んだ画像からサイズを取り出す
        const uploadWidth = fileImage.width,
          uploadHeight = fileImage.height;

        //幅200を超える画像は全て200へリサイズする。そうでない場合はリサイズしない。wnatWidthはリサイズしたいサイズのこと。
        const wantWidth = uploadWidth < 200 ? uploadWidth : 200,
          resizeRatio = uploadWidth / wantWidth,
          wantHeight = uploadHeight / resizeRatio;

        //canvasメソッドを使ってサイズ指定してdrawImage(略)でレンダリング
        const canvas = document.createElement("canvas");
        canvas.width = wantWidth;
        canvas.height = wantHeight;
        let ctx = canvas.getContext("2d");
        ctx.drawImage(fileImage, 0, 0, wantWidth, wantHeight);

     //canvasデータをblobデータへ変換し、受け取った結果を元にイメージファイルであるresizedImageFile変数を作る
        ctx.canvas.toBlob(
          async (blob) => {
            const resizedImageFile = new File([blob], file.name, {
              type: file.type,
              lastModified: Date.now()
            });

            const blobToUrl = URL.createObjectURL(blob); //blobをimgのsrc属性で使える形へ変換
            setUploadImg(blobToUrl); //リサイズした画像を表示
            uploadToFirestore(resizedImageFile); //resizedImageFileをFirestoreへ
          },
          file.type,
          1
        );
      };
// 省略

コードへ注釈を入れていますがそのままの通りになります。

1
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
3