ドラッグした画像ファイルをBase64に変換するReact用コンポーネント − react-image-base64

画像ファイルをリサイズする

最近のスマートフォンで撮影した写真は解像度が高くそのまま送信してしまうとサーバー側の負荷が高くなってしまったり、通信時間がかかってタイムアウトになってしまう問題が発生してしまいます。

その為、クライアント側(javascript側)で画像をリサイズして解像度を小さくしてからサーバー側へ送信する処理は、Webアプリケーションにおいて(特にフロントエンドにおいて)は必須な対応であると考えます。

使い方

$ yarn add -D react-image-base64
const [images, setImages] = useState({data: []});
const [errors, setErrors] = useState([]);
return (
  <div>
    <ReactImageBase64
      maxFileSize={10485760}
      thumbnail_size={100}
      drop={true}
      dropText="ファイルをドラッグ&ドロップもしくは"
      capture="environment"
      multiple={true}
      handleChange={data => {
        if (data.result) {
          let list =images.data
          list.push(data);
          setImages({data: list})
        } else {
          setErrors([...errors, data.messages]);
        }
      }}
    />
    { errors.map((error, index) => 
        <p className="error-message" key={index}>{error}</p>
      )
    }
  </div>
)

特徴

  1. ファイルを選択するとBase64に変換されたデータを返却する
  2. 画像のサイズを指定することでリサイズする
  3. HEIC形式の画像の場合はJPEGに変換する
  4. ドロップでのファイル選択が可能

以下は、読み込んだ画像の縦または横のピクセルが指定したサイズよりも大きい場合は、アスペクト比を維持したまま指定したサイズにリサイズをすることでサーバーへ送信するファイルサイズを小さくしています。

サイズを小さくして解像度を下げすぎると表示される画像の見た目が荒くなりますが、サイズが大きいと画面を表示する際に読み込みが遅くなってしまいます。

画像の見た目が気にならない程度の解像度にサイズをリサイズすることがポイントです。

const image = new Image()
const fr = new FileReader()
fr.onload = function(evt) {
  // リサイズする
  image.onload = function() {
    let width, height
    if (image.width > image.height) {
      // 横長の画像は横のサイズを指定値にあわせる
      const ratio = image.height / image.width
      width = props.thumbnail_size
      height = props.thumbnail_size * ratio
    } else {
      // 縦長の画像は縦のサイズを指定値にあわせる
      const ratio = image.width / image.height
      width = props.thumbnail_size * ratio
      height = props.thumbnail_size
    }
    // サムネ描画用canvasのサイズを上で算出した値に変更
    const canvas = document.createElement('canvas')
    canvas.id = 'canvas'
    canvas.width = width
    canvas.height = height
    const ctx = canvas.getContext('2d')
    if (ctx) {
      // canvasに既に描画されている画像をクリア
      ctx.clearRect(0, 0, width, height)
      // canvasにサムネイルを描画
      ctx.drawImage(image, 0, 0, image.width, image.height, 0, 0, width, height)
    }

    // canvasからbase64画像データを取得
    const base64 = canvas.toDataURL('image/jpeg')
    // base64からBlobデータを作成
    const bin = atob(base64.split('base64,')[1])
    const len = bin.length
    const barr = new Uint8Array(len)
    let i = 0
    while (i  len) {
      barr[i] = bin.charCodeAt(i)
      i++
    }
    const resizeBlob = new Blob([barr], { type: 'image/jpeg' })
    callback({
      fileName: fileName,
      ofileData: evt.target ? evt.target.result : null,
      fileData: base64,
      ofileSize: blob.size,
      fileSize: resizeBlob.size,
      fileType: resizeBlob.type,
    })
  }
  image.onerror = function() {
    errorCallback(['選択されたファイルをロードできません。'])
  }
  image.src = evt.target ? evt.target.result + '' : ''
}
fr.readAsDataURL(blob)

HEIC形式の画像を変換する

また、iPhone11以降で撮影した写真はHEICというブラウザで表示できない形式の画像ファイルで保存されます。

その為、HEIC形式の画像ファイルの場合はJPEGなどブラウザで表示できる形式に変換する必要があります。

heic2any というライブラリを利用することで、JPEGなどブラウザで表示可能な形式に変換することが可能です

heic2any({
  blob: file,
  toType: 'image/jpeg',
  quality: 1,
})
.then(function(rb) {
  const resultBlob = rb as Blob;
  const errors = validate(resultBlob)
  if (0 < errors.length) {
    errorCallback(errors)
    return
  }
  resize(
    file.name,
    resultBlob,
    function(res: { fileName: string; fileData: string; }) {
      res.fileName = file.name
      successCallback({
        ...res,
        result: true, 
        messages: ['正常終了'],
      })
    },
    function(errors: string[]) {
      errorCallback(errors)
      return
    },
  )
})

デモ

画像ファイルを選択すると元の画像とリサイズ後の画像が一覧に表示されます。元の画像に比べて、リサイズ後の方がサイズが小さくなっている事が判ると思います。

こちらにデモページを用意しているのでご利用ください。

ソースコード

ソースコードはGitHubにて公開しております。

https://isystk.github.io/react-image-base64

コメントを残す

入力エリアすべてが必須項目です。メールアドレスが公開されることはありません。

内容をご確認の上、送信してください。