パフォーマンス無関心で作ったサイトのパフォーマンスチューニング④ 画像のアップロード圧縮
はじめに
こんにちわ!こんばんわ!ご観覧ありがとうございます。
定期的に雑色な記事を投稿します。(タイトルは記事のメインの内容にしてますが)
基本的には抱えている問題を改善し、改善方法を記事にしてアップしようと考えています。
お題と経緯
静的サイトで何が一番パフォーマンスに影響がでるかは、この記事を書いてきた経験上画像という認識です。
作成しているサイトにはアップロードされた画像を読み込む画面も存在しているため、圧縮が必要と考えました。
今回の内容
画像の圧縮方法を記録していきたいと思います!
使用した技術
Category | Technology Stack |
---|---|
Frontend | Nextjs,React,Typescript |
Backend | Typescript,Node,CloudFunction |
Database | Firestore |
Design | XD |
前回の内容
前回記事
パフォーマンス無関心で作ったサイトのパフォーマンスチューニング③ FirebaseでNextjsをhosting(ImageタグとSSG併用難しい) - zare926のブログ
NextjsのImageを使用してFirebaseへデプロイする方法でした。
バージョンが低い状態でしか成功していないので、いつかAppRouterについても挑戦してみようと思っています。
browser-image-compressionとsharpの紹介
browser-image-compressionはクライアントサイドで圧縮できるライブラリです。
sharpはnodejsでサーバーサイドで圧縮するライブラリです。
browser-image-compression ライブラリ
まずはインストールしましょう
$ npm install browser-image-compression # または $ yarn add browser-image-compression
使いたい場所でimport
import imageCompression from 'browser-image-compression';`
で実際圧縮したい場合の例
const options = { maxSizeMB: 1, // この場合1Mです maxWidthOrHeight: 1920, //最大の高さが1920 useWebWorker: true, // Webワーカーを有効にしている }; const inputImage = async (e: React.ChangeEvent<HTMLInputElement>) => { if (e.target && e.target.files) { const file = e.target.files[0] const reader = new FileReader() const compressFile = await imageCompression(file, options) await imageUpload(compressFile) reader.readAsDataURL(compressFile) } }
optionに渡せる設定はドキュメント参照してください。
browser-image-compression - npm
とても簡単に圧縮できます!
クライアントサイドで特に重い処理などがなければ問題なく使えるライブラリだと思います。
sharp ライブラリ
前段としてNextのImageコンポーネントの画像圧縮に使われているライブラリです。
クライアントサイド側で圧縮することが似合わない場合はサーバーサイドで圧縮しましょう。
こちらもインストールから
npm install sharp # または yarn add sharp
モジュールじゃなくてrequire
const sharp = require('sharp');
圧縮の一例
// 入力画像のパス const inputImagePath = 'input.jpg'; // 出力画像のパス const outputImagePath = 'output.jpg'; // 圧縮設定 const compressionOptions = { quality: 70, // 画質の品質を指定 (0から100の範囲) progressive: true, // プログレッシブJPEGを有効にする optimizePalette: true, // PNGの場合、パレット最適化を有効にする fit: 'inside', // 画像のフィット方法を指定 background: 'white', // 透明な領域の背景色を指定 }; sharp(inputImagePath) .resize(800, 600) // 画像のリサイズ .toFile(outputImagePath, (err, info) => { if (err) { console.error(err); } else { console.log(info); } });
Webpなども変換できるようですね。
以下記事を参考にしました。
Node.jsのライブラリsharpの出力形式について | Simple is Beautiful.
FirebaseであればCloudFuntions側圧縮するということができそうですね。
const functions = require('firebase-functions'); const { Storage } = require('@google-cloud/storage'); const os = require('os'); const path = require('path'); const sharp = require('sharp'); const gcs = new Storage(); exports.compressImage = functions.storage.object().onFinalize(async (object) => { const bucket = gcs.bucket(object.bucket); const filePath = object.name; const fileName = path.basename(filePath); if (!filePath.startsWith('compressed/')) { // This check prevents infinite recursion const tempFilePath = path.join(os.tmpdir(), fileName); // Download the image from Firebase Storage await bucket.file(filePath).download({ destination: tempFilePath }); // Compress the image await sharp(tempFilePath).resize(800).toFile(tempFilePath); // Upload the compressed image back to Storage await bucket.upload(tempFilePath, { destination: `compressed/${fileName}`, }); // Clean up the temporary file return fs.unlinkSync(tempFilePath); } return null; });
(このコードはGPTに作ってもらったので、古いと思います。すいません。。。)
とは言ってもCloudFunctionsのv1は10MB、v2は32Mまでが受け取り可能なデータ量なので、なかなかないと思いますが、それ以上のデータを圧縮する場合は別の方法が必要ですね。
最後に
全然気にもしてなかったんですが、パフォーマンスの記事を書くようになってから画像に対する処理に敏感になりましたね。
いいこと!