ファイルダウンロードできるChrome拡張(Chrome Extension)をつくる

Chrome 拡張(Chrome Extension)でファイルダウンロードさせたい。
実装は簡単なんですが、JS を実行させるまでハマったのでメモ。

なお、「自作のChrome拡張を使うにはどうしたらいいの?」っていうのは書いてないです。
そこはクリアして書き始めた人向け。

Chrome 拡張の chrome.downloads を使う

Chrome拡張の設定ファイルであるmanifest.jsonpermission"donwloads"を設定するだけで使える。
のですが、ページ表示でcontent_scriptsから実行される JavaScript に登録するだけでは使えないのでハマりました。

chrome.downloadsを利用できるのはバックグラウンドで動作する JavaScript です。
ページ側で動作するcontent_scriptsからは直接使えないので、バックグラウンド側に情報を渡して動作させます。

ちなみに以前は Google Chrome 派でしたが、Chromium版Edge の垂直タブが便利すぎて乗り換えました。
Chromium 版 Edge でも chrome 拡張がそのまま使えます。
今回の動作確認も Chromium版Edgeで行っています。

必要なもの

chrome 拡張ではmanifest.jsonを書く必要があり、他は普通の JavaScript や HTML、CSS と一緒です。
今回は chrome.downloadsを利用したいので、フロント用とバックグラウンド用の 2 つの JavaScript ファイルを用意します。
ページ表示のタイミングで自動で動き、UIがないのでHTML、CSSは不要です。

  • manifest.json: chrome 拡張の定義
  • main.js: フロントで実行される JavaScript
  • background.js: フロントから呼び出される JavaScript

があれば動かせます。

開発環境の準備

VScode がおすすめですが、テキストエディタがあれば開発できます。

github 上のサンプル集からキーワード検索・コードをコピペして改造していくのが良いと思います。
https://github.com/GoogleChrome/chrome-extensions-samples にあるので、検索しやすいようにローカルに clone しておきましょう。

# 公式サンプルをよく参照するので、探しやすいようにcloneしておく
git clone https://github.com/GoogleChrome/chrome-extensions-samples.git

manifest.json の作成

公式のWelcome to Manifest V3に情報がありますが、正直欲しい情報を探しにくい・・・
manifest_versionの 2 と 3 では項目名が違ったりするので、使うサンプルのバージョンにも気をつけてください。

{
  "manifest_version": 3,
  "name": "myDownloader",
  "version": "0.0.1",
  "permissions": ["downloads"], // chrome.donwolad APIを利用する場合に必須
  "background": {
    "matches": ["https://bitto.jp/*"], // 開いたページのURLがマッチする場合に実行
    "service_worker": "background.js"
  },
  "content_scripts": [
    {
      "matches": ["https://bitto.jp/*"], // 開いたページのURLがマッチする場合に実行
      "js": ["main.js"]
    }
  ]
}

permissions には downloadsを設定しておきます。

main.js の作成

chrome 拡張を仕込みたいページ側で動作する JavaScript です。
ページを解析して対象のダウンロード URL を探す役割をします。
URL を探しますが、こちらからは直接chrome.downloadsを呼び出すことができません。
chrome.runtime.sendMessageを介して後述のbackground.jsにダウンロード情報を渡す必要があります。

今回はシンプルに、ページ表示のタイミングでダウンロードを開始する例を作ります。
chrome.runtime.sendMessageでは任意のオブジェクトを送信できます。
URL からダウンロードするだけなら、直接文字列で渡してもいいでしょう。

// background.jsを呼び出す関数
function startDl() {
  chrome.runtime.sendMessage({ url: "https://bitto.jp/cat.png" })
}

// 関数をwindow.onLoadのタイミングで実行させる
window.addEventListener('load', startDl)

background.js の作成

ただ DOM をいじるだけならcontent_scriptsだけで良いのですが、chrome.downloadsmanifest.jsonpermissionsで許可した上でバックグランドでしか使えません。
これが分かるまで main.js側でchrome.downloadsが触れず、ちょっとハマりました。

保存先のダウンロードファイル名を変更するには、chrome.downloads.onDeterminingFilenameを使います。
リスナは多重登録できないので、1つの関数でファイル名を解決させる必要があります。
保存先のパスはディレクトリ名も含められるので、サイトごとにファイルをまとめることもできます。

chrome.downloads.downloadがダウンロード ID を返すので、それに紐付いたファイル名で変更できます。

// main.jsから呼び出される
let fileNames = {}
chrome.runtime.onMessage.addListener(function (msg) {
  const { url } = msg
  chrome.downloads.download({ url }).then(id => {
    fileNames[id] = "neko.png" // idとファイル名を関連付け
  })
})

// ファイル名変更のためリスナを追加(1回だけ登録、変更しなくていいなら要らない)
chrome.downloads.onDeterminingFilename.addListener((ev, __suggest) => {
  const filename = fileNames[ev.id] // ダウンロードIDからファイル名を解決
  delete fileNames[ev.id]
  __suggest({ filename })
})

動作サンプル

NEKOHA YOSHIDA / chromeExtensionSample_downloads - GitLab

まとめ

manifest.jsonさえ書いてしまえばあとは普通のJavaScriptです。
思ってたより簡単に試すことができました!

参考サイト

Chrome Extensionを作ってみよう
https://np-complete.gitbook.io/chrome-extension/chrome-extensionwottemiyou

Welcome to Manifest V3 - ChromeDevelopers
https://developer.chrome.com/docs/extensions/mv3/intro/

GoogleChrome/chrome-extensions-sample - GitHub
https://github.com/GoogleChrome/chrome-extensions-samples

Subscribe to 猫好きが猫以外のことも書く

Don’t miss out on the latest issues. Sign up now to get access to the library of members-only issues.
jamie@example.com
Subscribe