react-image-base64 とは
react-image-base64 は、NPMで公開 されている画像アップロード用のReactコンポーネントです。
特徴
- Base64にエンコードされた画像データを取得することが出来ます。
- サイズを指定することで選択した画像をリサイズすることが出来ます。
- iPhoneで撮影されたHEIC形式の画像をJPEGに変換します。
- ドラッグ&ドロップでファイルの選択が可能です。
デモ
画像ファイルを選択すると元の画像とリサイズ後の画像が一覧に表示されます。元の画像に比べて、リサイズ後の方がサイズが小さくなっている事が判ると思います。
こちらにデモページを用意しているのでご利用ください。
使い方
$ 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>
)
画像ファイルのリサイズ機能
最近のスマートフォンで撮影した写真は解像度が高くそのまま送信してしまうとサーバー側の負荷が高くなってしまったり、通信時間がかかってタイムアウトになってしまう問題が発生してしまいます。
その為、クライアント側(javascript側)で画像をリサイズして解像度を小さくしてからサーバー側へ送信する処理は、Webアプリケーションにおいて(特にフロントエンドにおいて)は必須な対応であると考えます。
以下は、読み込んだ画像の縦または横のピクセルが指定したサイズよりも大きい場合は、アスペクト比を維持したまま指定したサイズにリサイズをすることでサーバーへ送信するファイルサイズを小さくしています。
サイズを小さくして解像度を下げすぎると表示される画像の見た目が荒くなりますが、サイズが大きいと画面を表示する際に読み込みが遅くなってしまいます。
画像の見た目が気にならない程度の解像度にサイズをリサイズすることがポイントです。
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形式をJPEGに変換する機能
また、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
},
)
})