Next.jsで画像処理を行いたいけれど、どのライブラリを使えばいいか迷っていませんか?本記事では、高速で使いやすいSharpライブラリについて、初心者の方にもわかりやすく解説します。
Sharpライブラリとは?
Sharpは、Node.js環境で動作する高性能な画像処理ライブラリです。Next.jsのようなサーバーサイドJavaScriptフレームワークと相性が良く、画像のリサイズや最適化を高速に行えます。
Sharpが選ばれる3つの理由
1. 圧倒的な処理速度
Sharpは、libvipsという画像処理エンジンを使用しています。従来よく使われていたImageMagickと比較して、4〜5倍も高速に画像を処理できます。大量の画像を扱うWebサイトでは、この速度差が大きなメリットになります。
2. メモリ効率の良さ
画像処理は通常、大量のメモリを消費します。しかしSharpは効率的なメモリ管理により、サーバーリソースを節約しながら動作します。これにより、複数のリクエストを同時に処理する際も安定したパフォーマンスを維持できます。
3. シンプルで直感的なAPI
メソッドチェーンという書き方で、複数の処理を連続して記述できます。コードが読みやすく、初心者でも理解しやすい設計になっています。
Sharpでできること
Sharpライブラリを使うと、以下のような画像処理が簡単に実装できます。
基本的な画像操作
画像のリサイズやクロッピング(切り抜き)は、Webサイトでよく使う機能です。例えば、ユーザーがアップロードした大きな画像をサムネイル用に小さくしたり、正方形に切り抜いたりできます。
回転や反転も簡単です。スマートフォンで撮影した写真の向きを自動で修正する際などに活用できます。
フォーマット変換と最適化
JPEGやPNGといった従来の画像形式から、WebPやAVIFなどの次世代フォーマットへの変換が可能です。これらの新しいフォーマットは、同じ画質でもファイルサイズを大幅に削減できるため、ページの読み込み速度改善に効果的です。
画質の調整も柔軟に行えます。用途に応じて適切な圧縮率を設定することで、見た目とファイルサイズのバランスを最適化できます。
高度な処理
複数の画像を重ね合わせて合成したり、ぼかしやシャープネスなどのフィルターを適用したりすることもできます。また、画像のメタデータ(撮影日時やカメラ情報など)の編集も可能です。
Next.jsプロジェクトへの導入方法
それでは、実際にNext.jsプロジェクトにSharpを導入してみましょう。
インストール
まず、npmまたはyarnでSharpをインストールします。ターミナルで以下のコマンドを実行してください。
npm install sharp
yarnを使用している場合は以下のコマンドです。
yarn add sharp
注意点:クライアントサイドでは使えません
重要な点として、Sharpはサーバーサイド専用のライブラリです。つまり、ブラウザ上では動作しません。Next.jsでは、API Routes、Server Actions、またはgetServerSidePropsなどのサーバーサイド機能内でのみ使用できます。
実践的な使用例
ここからは、実際のコード例を見ながら、Sharpの使い方を学んでいきましょう。
例1:基本的な画像リサイズ
最もシンプルな例として、画像を指定したサイズにリサイズする方法を見てみましょう。
import sharp from 'sharp';
// 画像を300x200ピクセルにリサイズ
await sharp('input.jpg')
.resize(300, 200)
.toFile('output.jpg');
このコードは、input.jpgを読み込んで300×200ピクセルにリサイズし、output.jpgとして保存します。非常にシンプルですね。
例2:アスペクト比を保持したリサイズ
実際の開発では、画像の縦横比を保ちながらリサイズしたいことが多いです。その場合は、以下のようにfitオプションを使います。
await sharp('input.jpg')
.resize(300, 200, {
fit: 'inside', // 指定サイズ内に収まるようにリサイズ
withoutEnlargement: true // 元画像より大きくしない
})
.toFile('output.jpg');
fitオプションには、inside(内側に収める)、outside(外側を覆う)、cover(切り抜いて埋める)などが指定できます。
例3:WebP形式への変換と圧縮
次世代画像フォーマットのWebPに変換し、同時に圧縮する例です。
await sharp('input.jpg')
.resize(800, 600)
.webp({ quality: 80 }) // WebP形式で品質80%
.toFile('output.webp');
quality値は0〜100で指定でき、80前後が一般的に推奨される値です。画質とファイルサイズのバランスが良い設定になります。
例4:Next.js API Routeでの実装
Next.jsのAPI Routeを使って、アップロードされた画像を処理する実例を見てみましょう。
// pages/api/image-upload.js
import sharp from 'sharp';
import formidable from 'formidable';
import fs from 'fs';
export const config = {
api: {
bodyParser: false, // フォームデータを手動で処理
},
};
export default async function handler(req, res) {
if (req.method !== 'POST') {
return res.status(405).json({ message: 'Method not allowed' });
}
// フォームデータをパース
const form = formidable({});
const [fields, files] = await form.parse(req);
const uploadedFile = files.image[0];
const originalPath = uploadedFile.filepath;
const outputPath = `./public/uploads/optimized-${Date.now()}.webp`;
try {
// 画像を処理
await sharp(originalPath)
.resize(1200, 1200, { fit: 'inside', withoutEnlargement: true })
.webp({ quality: 85 })
.toFile(outputPath);
// 元のファイルを削除
fs.unlinkSync(originalPath);
res.status(200).json({
message: 'Image processed successfully',
path: outputPath
});
} catch (error) {
res.status(500).json({ message: 'Error processing image', error: error.message });
}
}
この例では、アップロードされた画像を最大1200×1200ピクセルにリサイズし、WebP形式で保存しています。
例5:複数サイズのサムネイル生成
一つの画像から複数のサイズのサムネイルを同時に生成する例です。レスポンシブデザインで異なる画面サイズに対応する際に便利です。
const sizes = [
{ width: 320, name: 'small' },
{ width: 640, name: 'medium' },
{ width: 1280, name: 'large' }
];
await Promise.all(
sizes.map(size =>
sharp('input.jpg')
.resize(size.width)
.webp({ quality: 80 })
.toFile(`output-${size.name}.webp`)
)
);
Promise.allを使うことで、複数の画像処理を並行して実行し、効率的に処理できます。
よくある使用シーン
サムネイル自動生成
ブログやECサイトで、アップロードされた画像から自動的にサムネイルを生成する際に活用できます。一覧ページでは小さいサムネイルを表示し、詳細ページでは大きな画像を表示することで、ページの読み込み速度を最適化できます。
画像最適化パイプライン
ユーザーがアップロードした画像を自動的に最適化するシステムを構築できます。リサイズ、フォーマット変換、圧縮を組み合わせることで、Webパフォーマンスを大幅に改善できます。
動的なOGP画像生成
ブログ記事やページごとに異なるOGP画像(SNSでシェアされた際に表示される画像)を動的に生成する際にも使えます。テキストを画像に合成する機能を使えば、記事タイトルを含んだOGP画像を自動生成できます。
パフォーマンス最適化のコツ
適切な画質設定
WebPやJPEGの品質設定は、用途によって調整しましょう。写真は80〜85%、イラストやスクリーンショットは85〜90%が目安です。実際に見比べながら、最適な値を見つけることをお勧めします。
キャッシュの活用
処理済みの画像はキャッシュして再利用することで、サーバー負荷を軽減できます。同じ画像に対して何度もリサイズ処理を行わないよう、ファイル名やURLにハッシュ値を含めるなどの工夫が効果的です。
ストリーム処理の活用
大きなファイルを扱う場合は、ストリーム処理を使うとメモリ効率が向上します。
const readStream = fs.createReadStream('large-input.jpg');
const writeStream = fs.createWriteStream('output.webp');
readStream
.pipe(sharp().resize(800).webp())
.pipe(writeStream);
エラー処理のベストプラクティス
画像処理では、ファイルが破損していたり、想定外のフォーマットだったりすることがあります。適切なエラーハンドリングを実装しましょう。
try {
await sharp('input.jpg')
.resize(300, 200)
.toFile('output.jpg');
} catch (error) {
if (error.message.includes('Input file is missing')) {
console.error('ファイルが見つかりません');
} else if (error.message.includes('Input buffer contains unsupported image format')) {
console.error('サポートされていない画像形式です');
} else {
console.error('画像処理エラー:', error.message);
}
}
Next.jsの画像最適化機能との使い分け
Next.jsには、next/imageコンポーネントという強力な画像最適化機能が標準で搭載されています。では、いつSharpを使うべきでしょうか?
next/imageを使うべき場合
- フロントエンドで画像を表示するとき
- 自動的な遅延読み込みが必要なとき
- レスポンシブ画像の配信を簡単に実装したいとき
Sharpを使うべき場合
- ユーザーがアップロードした画像を事前処理するとき
- 複雑な画像加工(合成、フィルター適用など)が必要なとき
- ビルド時に大量の画像を一括処理したいとき
- next/imageでは対応できない特殊な要件があるとき
両者は競合する技術ではなく、補完し合う関係にあります。Sharpで画像を最適化してから、next/imageで配信するという組み合わせも効果的です。
まとめ
Sharpは、Next.jsでの画像処理において非常に強力なツールです。高速で使いやすく、様々な画像処理ニーズに対応できます。
本記事で紹介した基本的な使い方から始めて、徐々に高度な機能を試してみてください。画像最適化は、Webサイトのパフォーマンス向上に直結する重要な要素です。Sharpを活用して、ユーザーにとって快適なWebサイトを構築していきましょう。
参考リンク
初めは難しく感じるかもしれませんが、実際にコードを書いて試してみることが上達の近道です。ぜひ自分のプロジェクトでSharpを活用してみてください。