2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ブラウザだけで画像を一括圧縮!サーバー不要のImage Compressorを作った話

2
Posted at

u3198448477_Flat_design_illustration_for_a_blog_article_about_504d3da8-d517-4780-afc8-5e349f3ce1e4_2.png

はじめに

「画像のサイズが大きすぎてアップロードできない...」
「ブログ用の画像、毎回圧縮ツールにかけるの面倒だな...」

こんな経験、ありませんか?

Webサービスへの画像アップロード制限に引っかかったり、ページ読み込み速度の改善のために画像を軽くしたかったり。画像圧縮って地味に必要な場面が多いんですよね。

そこで今回、完全ブラウザ完結型の画像一括圧縮ツールを個人開発しました。サーバーへのアップロード不要で、プライバシーも安心。しかも複数画像を一気に処理できるという優れものです。

結論:このツールでできること

  • 複数画像の一括圧縮(PNG/JPEG/WebP対応)
  • 品質調整(1〜100%でスライダー操作)
  • 出力形式の変換(JPEG ⇔ PNG ⇔ WebP)
  • クリップボードからの貼り付け(Ctrl+V対応)
  • 圧縮結果のBefore/After比較
  • ZIPでの一括ダウンロード

全てブラウザ内で完結するため、画像がサーバーに送信されることはありません。

image.png

🔗 ツールはこちら: https://tools.easegis.jp/ja/tools/image/image-compressor

使い方(5ステップ)

1. 画像をアップロード

ファイルピッカーから選択するか、Ctrl+Vでクリップボードから直接貼り付けできます。複数ファイルの同時選択にも対応しています。

2. 出力形式を選択

JPEG / PNG / WebP から選べます。用途に応じて選択してください。

形式 特徴 おすすめ用途
JPEG 高圧縮・軽量 写真、ブログ画像
PNG 透明対応・可逆 ロゴ、UI素材
WebP 高品質・軽量 Webサイト全般

3. 品質を調整

スライダーで1〜100%の範囲で調整できます。数値が低いほど圧縮率が高くなりますが、画質も下がります。80%前後がバランスが良いですね。

4. 圧縮を実行

「全○枚を圧縮」ボタンをクリック。複数画像が並列処理されるので、大量の画像でもサクサク処理されます。

5. ダウンロード

個別ダウンロードか、ZIPでまとめてダウンロードを選べます。圧縮後の画像はクリップボードにコピーすることも可能です(PNG形式のみ)。

技術的な実装解説

Canvas APIによる画像圧縮

画像圧縮の核となるのは、ブラウザのCanvas APIです。

const compressImage = async (
  imgUrl: string,
  quality: number,
  format: 'image/jpeg' | 'image/png' | 'image/webp'
): Promise<{ blob: Blob; url: string }> => {
  return new Promise((resolve, reject) => {
    const img = new Image();
    img.crossOrigin = 'anonymous';

    img.onload = () => {
      const canvas = document.createElement('canvas');
      canvas.width = img.width;
      canvas.height = img.height;

      const ctx = canvas.getContext('2d');
      if (!ctx) {
        reject(new Error('Canvas context not available'));
        return;
      }

      ctx.drawImage(img, 0, 0);

      // ここがポイント!qualityパラメータで品質を制御
      canvas.toBlob(
        (blob) => {
          if (blob) {
            const url = URL.createObjectURL(blob);
            resolve({ blob, url });
          } else {
            reject(new Error('Blob creation failed'));
          }
        },
        format,
        quality / 100  // 0.0 〜 1.0 の範囲
      );
    };

    img.onerror = () => reject(new Error('Image load failed'));
    img.src = imgUrl;
  });
};

canvas.toBlob()の第3引数に品質を渡すことで、出力サイズを制御できます。これがブラウザネイティブの画像圧縮機能なんですよね。

複数画像の並列処理

Promise.allを使って複数画像を同時に処理しています。

const compressAll = async () => {
  setIsProcessing(true);

  try {
    const results = await Promise.all(
      images.map(async (img) => {
        const { blob, url } = await compressImage(
          img.originalUrl,
          quality,
          format
        );
        return {
          ...img,
          compressedUrl: url,
          compressedSize: blob.size,
          compressedBlob: blob,
        };
      })
    );

    setImages(results);
  } catch (error) {
    console.error('Compression failed:', error);
  } finally {
    setIsProcessing(false);
  }
};

ZIP生成(JSZip)

複数ファイルをまとめてダウンロードするために、JSZipライブラリを使用しています。

import JSZip from 'jszip';

const downloadAsZip = async () => {
  const zip = new JSZip();
  const folder = zip.folder('compressed-images');

  if (!folder) return;

  images.forEach((img, index) => {
    if (img.compressedBlob) {
      const ext = format.split('/')[1];  // 'image/jpeg' → 'jpeg'
      folder.file(`image_${index + 1}.${ext}`, img.compressedBlob);
    }
  });

  const blob = await zip.generateAsync({ type: 'blob' });

  // ダウンロードトリガー
  const url = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url;
  a.download = 'compressed-images.zip';
  a.click();
  URL.revokeObjectURL(url);
};

クリップボード連携

Clipboard APIを使って、画像の貼り付けとコピーを実装しています。

// 貼り付け(Ctrl+V対応)
useEffect(() => {
  const handlePaste = async (e: ClipboardEvent) => {
    const items = e.clipboardData?.items;
    if (!items) return;

    for (const item of items) {
      if (item.type.startsWith('image/')) {
        const file = item.getAsFile();
        if (file) {
          // FileReader でData URLに変換して状態に追加
          const reader = new FileReader();
          reader.onload = (e) => {
            const url = e.target?.result as string;
            // 画像リストに追加
          };
          reader.readAsDataURL(file);
        }
      }
    }
  };

  document.addEventListener('paste', handlePaste);
  return () => document.removeEventListener('paste', handlePaste);
}, []);

Known Issues & Tips

PNG形式の注意点

PNGは可逆圧縮のため、品質パラメータを下げても大幅なサイズ削減は期待できません。ファイルサイズを優先する場合は、JPEG or WebPへの変換をおすすめします。

クリップボードコピーの制限

Clipboard APIの制約により、クリップボードへのコピーはPNG形式のみ対応しています。JPEGやWebPでコピーしたい場合は、一度ダウンロードしてからお使いください。

ブラウザ互換性

WebP形式は、一部の古いブラウザ(IE11など)では表示できません。幅広い環境での表示が必要な場合は、JPEGかPNGを選択してください。

まとめ

今回作成したImage Compressorの特徴をまとめると:

  • ブラウザ完結でプライバシー安心
  • Canvas APIによる高速な画像処理
  • 複数画像の一括処理対応
  • クリップボード連携で作業効率UP

ぜひ使ってみてください!

🔗 ツールはこちら: https://tools.easegis.jp/ja/tools/image/image-compressor

2
0
1

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
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?