Chrome拡張機能:複数ファイルをZIPにまとめてダウンロードする


はじめに

やりたいこと

サムネイル表示されている画像ファイルをZIPにまとめてダウンロードする。

制限事項

たとえChrome拡張機能がダウンロードしたものでも、ローカルのファイルにはアクセスできない。

ゆえに、メモリ上の画像データからZIPデータを作成し、それをダウンロードする。ZIPの作成にはJSZipを使用した。

手順1:画像ファイルのパスを探す

サムネイルからリンクをたどってオリジナル画像のURLを取得する。

その際、画像ファイルが置かれているドメインを確認し、manifest.jsonのpermissionsに追加する。(Flickrの場合は、「https://c1.staticflickr.com/」など。)ワイルドカードが使えないので、連番が降られている場合はすべての番号を追加する必要がある。

手順2:画像ファイルのダウンロード

XMLHttpRequestのresponseTypearraybufferを指定することで、画像ファイルをバイナリデータとして取得できる。

background.js
const DownloadArraybuffer = (url) => {
  const p = new Promise(resolve => {
    const xhr = new XMLHttpRequest();
    xhr.open('GET', url, true);
    xhr.responseType = 'arraybuffer';
    xhr.addEventListener('loadend', () => {resolve(xhr.response);});
    xhr.send();
  });

  return p;
}

手順3:ZIPデータを作成する

JSZipオブジェクトを作成し、fileメソッドでダウンロードした画像データを追加する。最後にgenerateAsyncメソッドを実行すればZIPデータが作成される。

background.js
const ZipGenerateAsync = (zip) => {
  return new Promise((resolve, reject) => {
    zip.generateAsync({type: "blob"}).then(resolve);
  });
};

const CreateZipBlob = async (urlList) => {
  const zip = new JSZip();

  for(let i = 0; i < urlList.length; i++) {
    const imageFile = await DownloadArraybuffer(urlList[i]);
    const fileName = i + '.jpg'

    zip.file(fileName, imageFile, {binary: true});
  }

  return await ZipGenerateAsync(zip);
}

手順4:ZIPデータをダウンロードする

createObjectURLの引数にバイナリデータを渡すと、ダウンロードするためのURLを発行できる。ダウンロード後はrevokeObjectURLでURLを破棄し、バイナリデータを開放する。

background.js
const DownloadFile = (url, filename) => {
  return new Promise(resolve => {
    chrome.downloads.download({url: url, filename: filename}, downloadId => {
      resolve(downloadId);
    });
  });
};

const Download = async () => {
  ...
  const zipBlob = await CreateZipBlob(urlList);
  const zipUrl = window.URL.createObjectURL(blob);
  const zipName = 'aaa.zip'

  await DownloadFile(zipUrl, zipName);
  window.URL.revokeObjectURL(zipUrl);
}