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

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

新規登録して質問してみよう
ただいま回答率
85.37%
ダウンロード

リモートシステムからローカルシステムへとデータを受信する事、もしくはそのようなデータ転送を行う事をダウンロードと呼びます。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

jQuery

jQueryは、JavaScriptライブラリのひとつです。 簡単な記述で、JavaScriptコードを実行できるように設計されています。 2006年1月に、ジョン・レシグが発表しました。 jQueryは独特の記述法を用いており、機能のほとんどは「$関数」や「jQueryオブジェクト」のメソッドとして定義されています。

Q&A

解決済

3回答

19466閲覧

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

flaumig

総合スコア67

ダウンロード

リモートシステムからローカルシステムへとデータを受信する事、もしくはそのようなデータ転送を行う事をダウンロードと呼びます。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

jQuery

jQueryは、JavaScriptライブラリのひとつです。 簡単な記述で、JavaScriptコードを実行できるように設計されています。 2006年1月に、ジョン・レシグが発表しました。 jQueryは独特の記述法を用いており、機能のほとんどは「$関数」や「jQueryオブジェクト」のメソッドとして定義されています。

1グッド

2クリップ

投稿2020/02/14 05:31

編集2020/02/14 06:25

前提・実現したいこと

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

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

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

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

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

実現したいこと

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

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

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

発生している問題

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

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

ソース

JavaScript

1function downloadFile(method, url) { 2 const xhr = new XMLHttpRequest(); 3 4 //レスポンス取得時 5 xhr.onload = function (e) { 6 const res = xhr.response; 7 8 if (xhr.status != 200) { 9 //ステータスが200以外の場合(省略) 10 return; 11 } 12 13 if (res.type !== 'application/octet-stream') { 14 //レスポンスがバイナリ以外の場合(省略) 15 return; 16 } 17 18 //レスポンスヘッダからファイル名を取得する 19 const disposition = xhr.getResponseHeader('Content-Disposition'); 20 if (disposition && disposition.indexOf('attachment') !== -1) { 21 const filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/; 22 const matches = filenameRegex.exec(disposition); 23 if (matches != null && matches[1]) { 24 fileName = matches[1].replace(/['"]/g, ''); 25 } 26 } 27 28 //ブラウザごとのダウンロード処理 29 if (window.navigator.msSaveBlob) { 30 // for IE 31 window.navigator.msSaveBlob(res, fileName); 32 } else if (window.URL && window.URL.createObjectURL) { 33 // for Chrome & Firefox 34 const link = document.createElement('a'); 35 link.href = window.URL.createObjectURL(res); 36 link.download = decodeURI(fileName); 37 link.click(); 38 } 39 }; 40 41 //レスポンス受信の失敗時 42 xhr.onerror = function (e) { 43 //省略 44 }; 45 46 //タイムアウト時 47 xhr.ontimeout = function (e) { 48 //省略 49 } 50 51 //処理のキャンセル時 52 xhr.onabort = function (e) { 53 //省略 54 } 55 56 xhr.open(method, url, true); 57 xhr.setRequestHeader("X-Authorization", "認証情報"); 58 xhr.setRequestHeader("Content-Type", "application/json"); 59 xhr.responseType = "blob"; 60 xhr.timeout = 10000; 61 xhr.send(null); 62}

環境

JQuery 3.2.1
Chrome 78.0.3904.97

u.sayaka0112👍を押しています

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

ベストアンサー

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

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

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

別途プログレスバーなどを実装するのではなく、

JSだけで、このような事が可能なのかを知りたいです。

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

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

readyState プロパティが

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

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

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

投稿2020/02/14 08:03

AkitoshiManabe

総合スコア5434

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

flaumig

2020/02/14 08:15

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

0

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

投稿2020/02/14 05:38

yambejp

総合スコア116443

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yambejp

2020/02/14 05:40

参考 <script> window.addEventListener('DOMContentLoaded', function(e){ fetch(url).then(res => { const total = parseInt(res.headers.get('content-length'), 10); let loaded = 0; const reader = res.body.getReader(); const stream = new Response( new ReadableStream({ start(controller) { (async()=>{ let result = await reader.read(); while (!result.done) { const value = result.value; loaded += value.byteLength; document.querySelector('#debug').textContent=`${loaded} / `+parseInt(loaded/total*1000)/10+"%"; controller.enqueue(value); result = await reader.read(); } controller.close(); })(); } }) ); return stream; }).then(res=>res.blob()).then(res=>{ document.querySelector('#debug').innerHTML+="completed"; }); }); </script> <div id="debug"></div>
flaumig

2020/02/14 05:52 編集

早速のご回答ありがとうございます。 結論としてはブラウザ標準(今回はChrome)のダウンロード進行状況を出すことはできないので、 自分でプログレスバーを実装すると良い、ということでしょうか? せっかくの助言を頂きましたのに、こちらの認識が間違っていたのでしたら申し訳ありません。
yambejp

2020/02/14 05:55

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

2020/02/14 06:31 編集

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

2020/02/14 08:06

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

0

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

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

投稿2020/02/14 05:37

編集2020/02/14 05:40
maisumakun

総合スコア145930

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

flaumig

2020/02/14 06:13

早速のご回答ありがとうございます。 仰っていることは下記のようにページURLに直接APIを指定してダウンロードさせる、という方法でしょうか?  const url = "https://hoge.com/file/abc.zip";  window.open(url, '_blank'); サンプルのコードでは省略してしまったのですが、実はAPIリクエスト時 ヘッダに認証情報を持たせる必要がある為、こちらの方法は断念した次第です。 ソースを正しく記載しておらず、申し訳ありません。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.37%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問