--- title: Monitoring downloads slug: Monitoring_downloads tags: - Download Manager - Firefox 3 translation_of: Archive/Mozilla/Monitoring_downloads ---
Firefox 3 では、ダウンロード状況の監視がかつてなく簡単になりました。これまでのバージョンの Firefox でも実装は可能でしたが、ひとつのオブザーバを一度に登録することしかできませんでした。Firefox 3 では、ダウンロードの監視にいくつでもリスナーを利用できる新しい API が導入されました。
この記事では、Firefox 3 のダウンロードマネージャを利用してダウンロードを監視する方法を実演します。また、親切なおまけとして、Storage API を利用してデータベースに sqlite コマンドを発行する方法も実演します。その結果は、[ツール] メニューの [Download log] を選択することで開けるウィンドウ上で見ることができます。このウィンドウでは、サンプル用の拡張機能をインストールしてから行われたすべてのダウンロードが一覧表示されます。一覧には、ファイル名、ダウンロード開始時刻と終了時刻、ダウンロード速度、ダウンロード状況が表示されます。ファイルの取得元 URL を示すツールチップも含まれています。
拡張機能の読み込みが完了すると、すぐにいくつかの処理が行われます。具体的には、ダウンロードマネージャの nsIDownloadManager
インタフェースのインスタンスを取得し、データを保存するためのデータベースを作成するのに、これらの処理が必要となります。
onLoad: function() { // 初期化コード this.initialized = true; this.strings = document.getElementById("downloadlogger-strings"); this.dlMgr = Components.classes["@mozilla.org/download-manager;1"] .getService(Components.interfaces.nsIDownloadManager); this.dlMgr.addListener(downloadlogger); // データベースを開き、そのファイルをプロファイルディレクトリに保存します this.dbFile = Components.classes["@mozilla.org/file/directory_service;1"] .getService(Components.interfaces.nsIProperties) .get("ProfD", Components.interfaces.nsIFile); this.dbFile.append("downloadlogger.sqlite"); // ストレージサービスへのアクセスを取得し、データベースを開きます this.storageService = Components.classes["@mozilla.org/storage/service;1"] .getService(Components.interfaces.mozIStorageService); var dbConn = this.storageService.openDatabase(this.dbFile); // テーブルを作成します。既に存在する場合は失敗しますが、気にしません。 dbConn.executeSimpleSQL("CREATE TABLE items (source TEXT, size INTEGER," + " startTime INTEGER, endTime INTEGER," + " speed REAL, status INTEGER)"); dbConn.close(); },
これは非常に簡単な例です。ダウンロードマネージャのインスタンスは、後で再利用できるよう downloadlogger
オブジェクトのメンバー変数にキャッシュされ、addListener()
メソッドが呼び出されてダウンロード状況の監視が開始されます。データベースファイルが開かれ、sqlite の CREATE TABLE
コマンドが実行されてテーブルが作成されます。
最後に、データベースが閉じられます。
mozIStorageConnection
の close()
メソッドは Firefox 3 Alpha 8 で追加されました。Firefox の以前のバージョンでは、データベースを明示的に閉じる方法がありません。その代わり、ガベージコレクタが接続オブジェクトを破棄したときに閉じられます。上記のコードが実行されたら、ダウンロードの状況が変わるたびに onDownloadStateChange()
メソッドが呼び出されます。これは nsIDownloadProgressListener
インタフェースの一部です。
この部分のコードは以下のようになります:
onDownloadStateChange: function(aState, aDownload) { var statement; switch(aDownload.state) { case Components.interfaces.nsIDownloadManager.DOWNLOAD_DOWNLOADING: // 開始されたダウンロードのために新しい列を追加します。各列には取得元 URI、 // サイズ、開始時刻が含まれます。終了時刻とダウンロード速度は、まだ分からないため、 // 初めはいずれも 0 に設定します。 // 状況は、ダウンロードマネージャから提供されるものと同じ、状況を示す値になります。 var dbConn = this.storageService.openDatabase(this.dbFile); statement = dbConn.createStatement("REPLACE INTO items VALUES " + "(?1, ?2, ?3, 0, 0.0, 0)"); statement.bindStringParameter(0, aDownload.source.spec); statement.bindInt64Parameter(1, aDownload.size); statement.bindInt64Parameter(2, aDownload.startTime); statement.execute(); statement.reset(); dbConn.close(); break; // ダウンロードの完了 (失敗もしくは成功) を記録します case Components.interfaces.nsIDownloadManager.DOWNLOAD_FINISHED: case Components.interfaces.nsIDownloadManager.DOWNLOAD_FAILED: case Components.interfaces.nsIDownloadManager.DOWNLOAD_CANCELED: this.logTransferCompleted(aDownload); break; } },
ここで 4 つの進捗状況に注目してみましょう。aDownload.state
フィールドで示されるダウンロード状況が Components.interfaces.nsIDownloadManager.DOWNLOAD_DOWNLOADING
の場合、ファイルのダウンロードが開始されています。aDownload
オブジェクトは nsIDownload
オブジェクトです。
その場合、新しいファイルのために、データベースを開いて REPLACE INTO
sqlite コマンドを作成することで、データベースに新しい列を作成します。最初の 3 列は、ダウンロードオブジェクトから提供された、取得元 URI、ファイルサイズ、開始時刻フィールドの値に設定されます。残りの列の情報は、この時点では分からないため、ゼロに設定されます。
ダウンロードの進捗状況が、ダウンロードが完了、キャンセル、あるいは失敗したことを示した場合、logTransferCompleted
ルーチンを呼び出して、その進捗状況の変更を示すようログを更新します。この部分のコードは以下のようになります。
logTransferCompleted: function(aDownload) { var endTime = new Date(); // 現在時刻が終了時刻になります // REPLACE sqlite コマンドを発行して記録を更新します。同じ取得元 URI と開始時刻の // 記録を見つけたら、その記録内の終了時刻、サイズ、速度のエントリーを更新します。 // 取得元 URI と開始時刻の両方が一致することを確認することで、同じファイルについて // 複数ダウンロードがあっても、それぞれに記録を取ることができます。 var dbConn = this.storageService.openDatabase(this.dbFile); var statement = dbConn.createStatement("UPDATE items SET size=?1, " + "endTime=?2, speed=?3, status=?4 WHERE source=?5 and startTime=?6"); statement.bindInt64Parameter(0, aDownload.size); statement.bindInt64Parameter(1, endTime.getTime()); statement.bindDoubleParameter(2, aDownload.speed); statement.bindInt32Parameter(3, aDownload.state); statement.bindStringParameter(4, aDownload.source.spec); statement.bindInt64Parameter(5, aDownload.startTime); statement.execute(); statement.reset(); dbConn.close(); },
ここでは単純に、データベースを開いて、UPDATE
sqlite コマンドを作成、実行することで、完了したダウンロードと取得元 URI と開始時刻が一致するダウンロード項目を検索し、その情報を更新しています。同じ URI と開始時刻の記録を探すことで、ユーザが同じファイルを何度ダウンロードした場合も、正しく処理を行うことができます。
ダウンロードログウィンドウのコードは、downloadlogger_dlwindow
と呼ばれるオブジェクトの中で完結しています。これはシンプルな例なので、1 回限りのログウィンドウとなっており、それ以降のログの変更は監視していません。ウィンドウが開かれた時点でのダウンロードの状況を単純に表示するだけです。
つまり、ここでの処理はすべて load イベントハンドラだけで行うことができます。コードは以下の通りです。
onLoad: function() { // データベースを開きます this.dbFile = Components.classes["@mozilla.org/file/directory_service;1"] .getService(Components.interfaces.nsIProperties) .get("ProfD", Components.interfaces.nsIFile); this.dbFile.append("downloadlogger.sqlite"); // ストレージサービスへのアクセスを取得し、データベースを開きます this.storageService = Components.classes["@mozilla.org/storage/service;1"] .getService(Components.interfaces.mozIStorageService); var dbConn = this.storageService.openDatabase(this.dbFile); var loglist = document.getElementById("loglist"); var statement = dbConn.createStatement("SELECT * FROM items"); // テーブル内のすべての項目を取得します try { while (statement.executeStep()) { var row = document.createElement('listitem'); // 列にセルを追加します var cell = document.createElement('listcell'); var sourceStr = statement.getString(0); row.setAttribute("tooltiptext", sourceStr); sourceStr = sourceStr.slice(sourceStr.lastIndexOf("/")+1, sourceStr.length); cell.setAttribute("label", sourceStr); // 取得元 row.appendChild(cell); cell = document.createElement('listcell'); cell.setAttribute("label", (statement.getInt64(1) / 1024).toFixed(1) + "KB"); // サイズ cell.setAttribute("style", "text-align:right"); row.appendChild(cell); var theDate = new Date(statement.getInt64(2) / 1000); // 開始時刻 cell = document.createElement('listcell'); var dateStr = theDate.toLocaleString(); cell.setAttribute("label", dateStr); row.appendChild(cell); theDate = new Date(statement.getInt64(3)); // 終了時刻 cell = document.createElement('listcell'); dateStr = theDate.toLocaleString(); cell.setAttribute("label", dateStr); row.appendChild(cell); var speed = statement.getDouble(4) / 1024.0; cell = document.createElement('listcell'); cell.setAttribute("label", speed.toFixed(1) + "KB/sec"); cell.setAttribute("style", "text-align:right"); row.appendChild(cell); var status = statement.getInt32(5); var style = "color:black"; cell = document.createElement('listcell'); var statusStr; switch(status) { case 0: statusStr = "Downloading"; break; case 1: statusStr = "Complete"; style = "color:green"; break; case 2: statusStr = "Failed"; style = "color:red"; break; case 3: statusStr = "Canceled"; style = "color:purple"; break; case 4: statusStr = "Paused"; style = "color:blue"; break; case 5: statusStr = "Queued"; style = "color:teal"; break; case 6: statusStr = "Blocked"; style = "color:white background-color:red"; break; case 7: statusStr = "Scanning"; style = "color:silver"; break; default: statusStr = "Unknown"; break; } cell.setAttribute("label", statusStr); cell.setAttribute("style", style); row.appendChild(cell); loglist.appendChild(row); } } finally { statement.reset(); dbConn = null; } }
このコードは至ってシンプルです。初めに、ログ情報が含まれる sqlite データベースを開いた後、SELECT
SQL 構文を作成し、データベースからすべてのエントリーを取得します。
複数の結果を繰り返し処理するために、mozIStorageStatement
オブジェクトの executeStep()
メソッドを呼び出す while
ループを使っています。このメソッドが呼び出されるたびに、結果から 1 つの列が取得されます。
その後、リスト列オブジェクトが作成され、検索結果の各エントリーが取得されて適切なリストセルに挿入されます。
上記のコードからいくつか興味深い点を取り上げてみましょう。
mozIStorageStatement
は、getString()
、getDouble()
、getInt64()
といった、検索結果を取得するためのデータ取得ルーチンをいくつか持っています。これらのメソッドは、取得したい値を持つ列のゼロベースのインデックス番号をパラメータとして取ります。
Date
オブジェクトを作成する前に、1000 で割られていることに注意してください。これは、データベースに保存されている時間の精度を、JavaScript で使用される値に変換するためです。
style
属性に text-align:right
を設定しています。
この拡張機能を改良するためにできる、一見して分かることがいくつかあります。ダウンロードマネージャやストレージ API の使い方を学んでいるなら、以下のようなことを、練習のために調べてみると良いでしょう。
Storage, nsIDownloadManager
, nsIDownload
, nsIDownloadProgressListener