web-dev-qa-db-ja.com

JavaScriptでファイルのダウンロードを開始する

サイトにファイルのダウンロードリンクがあるとしましょう。

これらのリンクをクリックすると、AJAXリクエストをサーバーに送信し、サーバーはURLをファイルの場所とともに返します。

私がやりたいのは、応答が戻ったときにファイルをダウンロードするようにブラウザに指示することです。これを行うポータブルな方法はありますか?

54
Vasil

このライブラリを試してください https://github.com/PixelsCommander/Download-File-JS これは、「ダウンロード可能な限り最高のエクスペリエンスをもたらすためのメソッドの属性と組み合わせ。

ここで説明- http://pixelscommander.com/en/javascript/javascript-file-downliading-ignore-content-type/

JavaScriptでダウンロードを開始するための理想的なコードのようです。

4
PixelCommander

そのようにします。最初にこのスクリプトを追加します。

<script type="text/javascript">
function populateIframe(id,path) 
{
    var ifrm = document.getElementById(id);
    ifrm.src = "download.php?path="+path;
}
</script>

これをダウンロードボタンが必要な場所に配置します(ここではリンクのみを使用します)。

<iframe id="frame1" style="display:none"></iframe>
<a href="javascript:populateIframe('frame1','<?php echo $path; ?>')">download</a>

ファイル「download.php」(サーバーに配置する必要があります)には、次のものが含まれています。

<?php 
   header("Content-Type: application/octet-stream");
   header("Content-Disposition: attachment; filename=".$_GET['path']);
   readfile($_GET['path']);
?>

したがって、リンクをクリックすると、非表示のiframeがソースファイル「download.php」を取得/開きます。パスを取得パラメーターとして使用します。これが最良の解決策だと思います!

このソリューションのPHPの部分は単純なデモンストレーションであり、非常に安全ではない可能性があります。事前定義されたセットだけでなく、任意のファイルをダウンロードできます。 API資格情報などを含む可能性のあるサイト自体のソースコードの一部をダウンロードできます。

24
roulio

オープンソースを作成しました jQuery File Downloadプラグイン (- 例のデモ )( GitHub ) iframeでも同様に機能しますが、便利な機能がいくつかあります。

  • ユーザーがファイルのダウンロードを開始したページと同じページを離れることはありません。この機能は、最新のWebアプリケーションにとって重要になりつつあります
  • テスト済みのクロスブラウザサポート(モバイルを含む!)
  • JQueryのPOST APIと同様の方法でAJAXおよびGETリクエストをサポートします
  • successCallbackおよびfailCallback関数を使用すると、どちらの状況でもユーザーに表示される内容を明示できます。
  • 開発者はjQuery UIと組み合わせて、ファイルのダウンロードが発生していることをユーザーに伝えるモーダルを簡単に表示したり、ダウンロードの開始後にモーダルを解散したり、エラーが発生したことをわかりやすい方法でユーザーに通知することもできます。この例については、デモを参照してください。
18
John Culviner

回答を読む-受け入れられたものも含めて、GETを介してreadfileに直接パスを渡すことのセキュリティへの影響を指摘したいと思います。

一部の人には自明のように見えるかもしれませんが、このコードを単にコピー/貼り付けするだけの人もいます。

<?php 
   header("Content-Type: application/octet-stream");
   header("Content-Disposition: attachment; filename=".$_GET['path']);
   readfile($_GET['path']);
?>

このスクリプトに「/ path/to/fileWithSecrets」のようなものを渡すとどうなりますか?指定されたスクリプトは、webserverユーザーがアクセスできるファイルを喜んで送信します。

これを防ぐ方法については、この説明を参照してください。 ファイルパスが特定のサブディレクトリ内にあることを確認するにはどうすればよいですか?

14
DanielKhan

これが独自のサーバーアプリケーションの場合、次のヘッダーを使用することをお勧めします

Content-disposition: attachment; filename=fname.ext

これにより、ブラウザはファイルをダウンロードし、ブラウザウィンドウにレンダリングしません。

10
fasih.rana

JavaScriptからwindow.location.href = new_urlを呼び出すだけで、ユーザーがアドレスバーに入力したとおりにブラウザーがそのURLにリダイレクトされます。

7
Gareth

Maxnkが言及している方法には同意しますが、ブラウザーにURLのダウンロードを自動的に強制することを再検討することができます。バイナリファイルでは正常に機能する場合がありますが、他の種類のファイル(テキスト、PDF、画像、ビデオ)の場合、ブラウザはディスクに保存するのではなくウィンドウ(またはIFRAME)でレンダリングする場合があります。

最終的なダウンロードリンクを取得するために本当にAjax呼び出しを行う必要がある場合、DHTMLを使用して(ajax応答から)ダウンロードリンクをページに動的に書き出すのはどうでしょうか。これにより、ユーザーはそれをクリックしてダウンロード(バイナリの場合)するか、ブラウザで表示するか、リンクの[名前を付けて保存]を選択してディスクに保存できます。それは余分なクリックですが、ユーザーはより多くのコントロールを持っています。

3
Marc Novakowski

トップ投票の回答のセキュリティ上の欠陥を回避するには、iframe srcを(中間のphpファイルの代わりに)目的のファイルに直接設定し、ヘッダー情報を.htaccessファイルに設定します。

<Files *.apk>
     ForceType application/force-download
     Header set Content-Disposition attachment
     Header set Content-Type application/vnd.Android.package-archive
     Header set Content-Transfer-Encoding binary
</Files>
3
Matt K

一番上の答えに関連して、セキュリティリスクの可能な解決策があります。

<?php
     if(isset($_GET['path'])){
         if(in_array($_GET['path'], glob("*/*.*"))){
             header("Content-Type: application/octet-stream");
             header("Content-Disposition: attachment; filename=".$_GET['path']);
             readfile($_GET['path']);
         }
     }
?>

Glob()関数を使用して(ダウンロードするファイルから1つ上のフォルダーのパスでダウンロードファイルをテストしました)ダウンロードが許可されているファイルのクイック配列を作成し、それに対して渡されたパスを確認できました。これにより、グラブされるファイルが機密性の高いものではないことが保証されるだけでなく、同時にファイルの存在もチェックされます。

〜注:Javascript/HTML〜

HTML:

<iframe id="download" style="display:none"></iframe>

そして

<input type="submit" value="Download" onclick="ChangeSource('document_path');return false;">

JavaScript:

<script type="text/javascript">
    <!--
        function ChangeSource(path){
            document.getElementByID('download').src = 'path_to_php?path=' + document_path;
        }
    -->
</script>
2

ページに非表示のiframeを作成し、サーバーから受け取ったURLにsrcを設定することをお勧めします-ダウンロードはページの再読み込みなしで開始されます。

または、現在のdocument.location.hrefを受信したURLアドレスに設定することもできます。ただし、要求されたドキュメントが実際に存在しない場合、ユーザーにエラーが表示される可能性があります。

2
maxnk

ブラウザーを別のwindow.location.hrefにリダイレクトするだけでよいのに、なぜサーバー側のものを作成するのですか?

これは?file = QueryString( この質問から取得 )を解析し、1秒でそのアドレスにユーザーをリダイレクトするコードです(Androidブラウザーでも動作します):

<script type="text/javascript">
    var urlParams;
    (window.onpopstate = function () {
        var match,
        pl = /\+/g,  // Regex for replacing addition symbol with a space
        search = /([^&=]+)=?([^&]*)/g,
        decode = function (s) { return decodeURIComponent(s.replace(pl, " ")); },
        query = window.location.search.substring(1);

        urlParams = {};
        while (match = search.exec(query))
            urlParams[decode(match[1])] = decode(match[2]);
    })();

    (window.onload = function() {
        var path = urlParams["file"];
        setTimeout(function() { document.location.href = path; }, 1000);
    });
</script>

プロジェクトにjQueryがある場合は、これらのwindow.onpopstateおよびwindow.onloadハンドラーを確実に削除し、$(document).ready(function(){});

1
nikib3ro

ポップアップウィンドウを開くには、window.open()をお勧めします。ダウンロードの場合、ウィンドウは表示されず、ファイルが取得されます。 404または何かがある場合、ユーザーは新しいウィンドウにそれを表示します(したがって、作業は煩わされませんが、エラーメッセージが表示されます)。

1
Vilx-