質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.62%

JSでのファイルダウンロード時、ブラウザのダウンロードバーにダウンロードの進行状況を表示したい

解決済

回答 3

投稿 編集

  • 評価
  • クリップ 2
  • VIEW 2,535

flaumig

score 52

前提・実現したいこと

JSでファイルのダウンロード処理を実装しており、
その処理は下記のように動作しています。

  1. ユーザが画面上のダウンロードボタンをクリック(非同期でファイルデータ取得APIリクエストを行う)
  2. ファイルの保存ダイアログが開く
  3. ユーザがダイアログの「保存」ボタンをクリック
  4. ブラウザのダウンロードバーに保存されたファイルが表示される

ダウンロード対象のファイルは取得用APIが存在し、レスポンスとしてファイルのバイナリデータを返します。

しかしサイズの大きいファイルをダウンロードしようとしたところ、
上記1.と2.の間(APIレスポンスの取得が完了するまでの間)、ブラウザ上に何も表示されないため
「ユーザビリティが悪い。よくあるダウンロード処理のようにボタンを押したらすぐにブラウザ標準機能のダウンロードバーを表示し、
ダウンロードの進行状況が分かるようにしてほしい(下記イメージ)」との指摘を受けました。

■図1
Chrome標準のダウンロードバーで進行状況が表示される事
ダウンロードバー
※これは参考画像であり、私が作成しているサイトの画像ではありません

実現したいこと

下記のような流れになるのが理想的です。

  1. ユーザが画面上のダウンロードボタンをクリック(非同期でファイルデータ取得APIリクエストを行う)
  2. ファイルの保存ダイアログが開く
  3. ユーザがダイアログの「保存」ボタンをクリック
  4. ブラウザのダウンロードバーが表示される(■図1参照)…ここでユーザはダウンロードの進行状況が分かる
  5. ダウンロード完了後、ダウンロードバーから保存されたファイルを開ける

別途プログレスバーなどを実装するのではなく、
JSだけで、このような事が可能なのかを知りたいです。

発生している問題

下記のような実装を行っていますが、
この状態では、一旦バイナリデータを全て受け取ってからwindow.URL.createObjectURLでオブジェクトを作成し、
ダウンロードさせているため、進行中の表示を行うことが出来ません。

他の実装方法があるのかと調べてみましたが、恥ずかしながら見つけることができませんでした。
もしこちらの要望を満たす方法をご存じの方がいらっしゃいましたら、ご教示頂けますようお願い致します。

ソース

function downloadFile(method, url) {
  const xhr = new XMLHttpRequest();

  //レスポンス取得時
  xhr.onload = function (e) {
    const res = xhr.response;

    if (xhr.status != 200) {
      //ステータスが200以外の場合(省略)
      return;
    }

    if (res.type !== 'application/octet-stream') {
      //レスポンスがバイナリ以外の場合(省略)
      return;
    }

    //レスポンスヘッダからファイル名を取得する
    const disposition = xhr.getResponseHeader('Content-Disposition');
    if (disposition && disposition.indexOf('attachment') !== -1) {
      const filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
      const matches = filenameRegex.exec(disposition);
      if (matches != null && matches[1]) {
        fileName = matches[1].replace(/['"]/g, '');
      }
    }

    //ブラウザごとのダウンロード処理
    if (window.navigator.msSaveBlob) {
      // for IE
      window.navigator.msSaveBlob(res, fileName);
    } else if (window.URL && window.URL.createObjectURL) {
      // for Chrome & Firefox
      const link = document.createElement('a');
      link.href = window.URL.createObjectURL(res);
      link.download = decodeURI(fileName);
      link.click();
    }
  };

  //レスポンス受信の失敗時
  xhr.onerror = function (e) {
    //省略
  };

  //タイムアウト時
  xhr.ontimeout = function (e) {
    //省略
  }

  //処理のキャンセル時
  xhr.onabort = function (e) {
    //省略
  }

  xhr.open(method, url, true);
  xhr.setRequestHeader("X-Authorization", "認証情報");
  xhr.setRequestHeader("Content-Type", "application/json");
  xhr.responseType = "blob";
  xhr.timeout = 10000;
  xhr.send(null);
}

環境

JQuery 3.2.1
Chrome 78.0.3904.97

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 3

checkベストアンサー

+3

Chrome標準のダウンロードバーで進行状況が表示される事

「ブラウザ標準のダウンロードファイル管理機能」は、単純にGET要求でダウンロードできる(Range要求/レジュームダウンロード対応)のファイルに限られます。
パラメータ指定が伴いPOST要求が求められる、ウェブサービスのAPIの応答で得られるファイルの場合には不可能です。

乱暴な表現ですが、「ブラウザ標準のダウンロードファイル管理機能はセキュリティ不要ファイルだけが対応できるもの」です。

別途プログレスバーなどを実装するのではなく、
JSだけで、このような事が可能なのかを知りたいです。

このような事 を実現する代替案が、「別途プログレスバーなどを実装する」ことです。

古くからあるダウンロード状況を捉えるには、onreadystatechange イベントを使っていました。

readyState プロパティが 

  1. HEADERS_RECEIVED のときにヘッダ情報を取得(Content-Length で総量を知る)
  2. LOADING のときに進捗状況を表示(進捗率を得る)
  3. DONE で完了 (onload イベント発火と同じタイミング)

現在は MDN XMLHttpRequest のprogress イベントを実装することになると思います。

あくまでも、代替案となります。ご参考になれば。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/02/14 17:15

    ご回答ありがとうございます。
    まさに求めていた回答でした。
    とても助かりました。

    キャンセル

0

ダウンロード対象のファイルは取得用APIが存在し、レスポンスとしてファイルのバイナリデータを返します。

なら、JavaScriptからXHRでリクエストを行うのではなく、ブラウザ遷移でそこにたどり着かせるようにする(たとえば、window.openで別タブ作成するなど)という方法も考えられます。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/02/14 15:13

    早速のご回答ありがとうございます。
    仰っていることは下記のようにページURLに直接APIを指定してダウンロードさせる、という方法でしょうか?

     const url = "https://hoge.com/file/abc.zip";
     window.open(url, '_blank');

    サンプルのコードでは省略してしまったのですが、実はAPIリクエスト時
    ヘッダに認証情報を持たせる必要がある為、こちらの方法は断念した次第です。
    ソースを正しく記載しておらず、申し訳ありません。

    キャンセル

0

「プログレス」で検索するといろいろヒットすると思いますよ

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/02/14 14:55

    非同期処理の話ですよね?
    プログレスは自前で用意するのが妥当です
    全体量とすでにダウンロードした量は私のサンプルを見ればわかると
    思いますので、プログレスバーなどは適当なライブラリをつかっても
    よいでしょう

    キャンセル

  • 2020/02/14 15:31 編集

    すみません。知りたい事が正しく伝えられておりませんでした。

    私の知りたいことはプログレスバーの実装方法ではなく
    「JSを利用してChromeの機能であるダウンロードバーでダウンロード状況が分かるようにしたい。それは可能か?不可能か?」
    という点でした。
    こちらの記述が不足していたせいでお手数をおかけして申し訳ありません。

    キャンセル

  • 2020/02/14 17:06

    ダウンロード処理をしていないので、標準機能のダウンロードバーは
    使えないと考えるのが普通です

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.62%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る