ajaxリクエストを使用してファイルをダウンロードする


95

ボタンをクリックしたときに「ajaxダウンロードリクエスト」を送信したいので、次の方法で試しました。

javascript:

var xhr = new XMLHttpRequest();
xhr.open("GET", "download.php");
xhr.send();

download.php:

<?
header("Cache-Control: public");
header("Content-Description: File Transfer");
header("Content-Disposition: attachment; filename= file.txt");
header("Content-Transfer-Encoding: binary");    
readfile("file.txt");
?>

期待どおりに動作しない場合、どうすればよいですか?前もって感謝します


2
これは機能しません。[この質問] [1]を参照してください。[1]:stackoverflow.com/questions/8771342/...
olegsv

2
実行location.href='download.php';
ムーサ

回答:


92

2015年4月27日更新

HTML5シーンの登場は、 ダウンロード属性です。それはだ、サポート FirefoxとChromeで、すぐにIE11に来て。必要に応じて、window.locationダウンロードするファイルがサイトと同じオリジンにある限り、AJAXリクエストの代わりに(またはを使用して)使用できます。

あなたはいつでもAJAXリクエスト/ window.locationフォールバックを行うことができますJavaScriptdownloadサポートいるかどうかをテストし、サポートされていない場合は呼び出しに切り替えることで、ますwindow.location

元の答え

物理的にファイルに移動してダウンロードを要求する必要があるため、AJAXリクエストでダウンロードプロンプトを開くことはできません。代わりに、success関数を使用して、download.phpに移動できます。これにより、ダウンロードプロンプトが開きますが、現在のページは変更されません。

$.ajax({
    url: 'download.php',
    type: 'POST',
    success: function() {
        window.location = 'download.php';
    }
});

これで問題は解決window.locationしますが、AJAXリクエストを完全に使用して回避することをお勧めします。


39
これはリンクを2回呼び出しませんか?私は同様のボートに乗っています...ヘッダーで多くのセキュリティ情報を渡していて、成功関数でファイルオブジェクトを解析できますが、ダウンロードプロンプトをトリガーする方法がわかりません。
user1447679 2015

3
このページは2回呼び出されるため、そのページのデータベースにクエリを実行している場合、これはDBへの2回のトリップを意味します。
mmmmmm 2016

1
:@ user1447679代替ソリューションを参照stackoverflow.com/questions/38665947/...
ジョン

1
これが私にどのように役立ったかを説明しましょう...この例はもっと完全だったかもしれません。「download.php?get_file = true」または何かを使用して...フォームの送信時にいくつかのエラーチェックを実行し、csvファイルを作成するajax関数があります。エラーチェックが失敗した場合は、失敗した理由を返す必要があります。CSVを作成する場合は、親に「先に進んでファイルをフェッチする」ように指示しています。これを行うには、フォーム変数を使用してajaxファイルに送信し、次に「作成したばかりのファイルを渡して」と言ってDIFFERENTパラメータを同じファイルに送信します(パス/名前はajaxファイルにハードコードされています)。
スコット

4
しかし、それは要求を2回送信します、それは適切ではありません
Dharmendrasinh Chudasama

43

これには実際にはajaxは必要ありません。ボタンのhrefとして「download.php」を設定した場合、またはリンクでない場合は、次のように使用します。

window.location = 'download.php';

ブラウザはバイナリダウンロードを認識し、実際のページをロードするのではなく、ファイルをダウンロードとして提供する必要があります。


3
変更に使用しているプログラミング言語window.location JavaScriptです。
mikemaccana 14

1
あなたは正しい@mikemaccana、私は実際にはajaxを意味した:)。
Jelle Kralt 2014

2
解決策を模索してきましたが、これはとてもエレガントで完璧です。どうもありがとうございます。
ヤンシュンテイ

2
もちろん、このソリューションは、すでに存在する静的ファイルである場合にのみ機能します。
オキアミ

1
サーバーがエラーで応答した場合でも、ブラウザーによってエラーページにリダイレクトされずにメインページにとどまる方法はありません。少なくとも、これはwindow.locationの結果は、404を返したときにクロムが何をするかである
イアン

43

ブラウザにファイルをダウンロードさせるには、次のようなリクエストを行う必要があります。

 function downloadFile(urlToSend) {
     var req = new XMLHttpRequest();
     req.open("GET", urlToSend, true);
     req.responseType = "blob";
     req.onload = function (event) {
         var blob = req.response;
         var fileName = req.getResponseHeader("fileName") //if you have the fileName header available
         var link=document.createElement('a');
         link.href=window.URL.createObjectURL(blob);
         link.download=fileName;
         link.click();
     };

     req.send();
 }

7
これは私にとってはうまくいきますが、Firefoxでは、ファイルを自動的にダウンロードするために、オンザフライで作成するのではなく、最初にDOMに<a>タグを配置してリンクとして参照する必要がありました。
Erik Donohoo、2017年

動作しますが、実行中にファイルが作成されている場合はどうなりますか?ファイルが既に作成されている場合のようには機能しません。
ディエゴ

@Taha私はEdgeでこれをテストしましたが、うまくいったようです。IEについては知らない。私のクライアントはIEユーザーをターゲットにしていません;-)私は幸運ですか?:D
Sнаđошƒаӽ

1
@ErikDonohoo <a>JSで「オンザフライ」でタグを作成することもできますが、に追加する必要がありますdocument.body。あなたは確かに同時にそれを隠したいと思います。
マートンタマシュ

@Joãoに感謝します。このソリューションを非常に長い間探していました。
Abhishek Gharai

20

Chrome、Firefox、Edge、IE11でテストされたクロスブラウザーソリューション。

DOMで、非表示のリンクタグを追加します。

<a id="target" style="display: none"></a>

次に:

var req = new XMLHttpRequest();
req.open("GET", downloadUrl, true);
req.responseType = "blob";
req.setRequestHeader('my-custom-header', 'custom-value'); // adding some headers (if needed)

req.onload = function (event) {
  var blob = req.response;
  var fileName = null;
  var contentType = req.getResponseHeader("content-type");

  // IE/EDGE seems not returning some response header
  if (req.getResponseHeader("content-disposition")) {
    var contentDisposition = req.getResponseHeader("content-disposition");
    fileName = contentDisposition.substring(contentDisposition.indexOf("=")+1);
  } else {
    fileName = "unnamed." + contentType.substring(contentType.indexOf("/")+1);
  }

  if (window.navigator.msSaveOrOpenBlob) {
    // Internet Explorer
    window.navigator.msSaveOrOpenBlob(new Blob([blob], {type: contentType}), fileName);
  } else {
    var el = document.getElementById("target");
    el.href = window.URL.createObjectURL(blob);
    el.download = fileName;
    el.click();
  }
};
req.send();

良いジェネリックコード。@leoのようなカスタムヘッダーを追加して、このコードを改善できますAuthorizationか?
vibs2006

1
@leoに感謝します。そのhelpful.Alsoあなたは何を追加することをお勧めしますwindow.URL.revokeObjectURL(el.href);el.click()
vibs2006

コンテンツの配置でUTF8以外のファイル名が指定されている場合、ファイル名は正しくありません。
Mathew Alden

14

可能です。たとえば、.csvファイルが作成された直後に、ajax関数内からダウンロードを開始できます。

連絡先のデータベースを.csvファイルにエクスポートするajax関数があります。それが完了すると、自動的に.csvファイルのダウンロードが開始されます。したがって、responseTextを取得してすべてがOKになった後、ブラウザを次のようにリダイレクトします。

window.location="download.php?filename=export.csv";

私のdownload.phpファイルは次のようになります:

<?php

    $file = $_GET['filename'];

    header("Cache-Control: public");
    header("Content-Description: File Transfer");
    header("Content-Disposition: attachment; filename=".$file."");
    header("Content-Transfer-Encoding: binary");
    header("Content-Type: binary/octet-stream");
    readfile($file);

?>

ページの更新は一切行われず、ファイルのダウンロードが自動的に開始されます。

-次のブラウザでテスト済み:

Chrome v37.0.2062.120 
Firefox v32.0.1
Opera v12.17
Internet Explorer v11

1
これは危険なセキュリティの問題ではありませんか?
ミカエルベルジェロンネロン

@MickaelBergeronNéronなんで?
ペドロ・スーザ

5
だれでもdownload.php?filename = [something]を呼び出して、いくつかのパスとファイル名(特に一般的なもの)を試すことができ、これはプログラムまたはスクリプト内のループ内でも実行できるためです。
ミカエルベルジェロンネロン

.htaccessはそれを避けませんか?
ペドロ・スーザ

9
@PedroSousa ..いいえ。htaccessは、Apacheを介したファイル構造へのアクセスを制御します。アクセスがPHPスクリプトに到達したので、htaccessはその役割を停止します。これは非常に大きなセキュリティホールです。実際、PHP(およびその実行中のユーザー)が読み取ることができるすべてのファイルは、readfileに配信される可能性があります...要求されたファイルを常にサニタイズして読み取る必要があります
Prof83


0

ヘッダーからファイル名をデコードすることはもう少し複雑です...

    var filename = "default.pdf";
    var disposition = req.getResponseHeader('Content-Disposition');

    if (disposition && disposition.indexOf('attachment') !== -1) 
    {
       var filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
       var matches = filenameRegex.exec(disposition);

       if (matches != null && matches[1]) 
           filename = matches[1].replace(/['"]/g, '');
    }

3
コードブロック全体をフォーマットし、今後の読者の利益のためにプロセスに追加の説明を提供してください。
mickmackusa 2017年

0

このソリューションは上記のものとさほど変わりませんが、私にとっては非常にうまく機能し、クリーンだと思います。

ファイルサーバー側をbase64エンコードし(PHPを使用している場合はbase64_encode())、base64エンコードされたデータをクライアントに送信することをお勧めします

クライアントでこれを行います:

 let blob = this.dataURItoBlob(THE_MIME_TYPE + "," + response.file);
 let uri = URL.createObjectURL(blob);
 let link = document.createElement("a");
 link.download = THE_FILE_NAME,
 link.href = uri;
 document.body.appendChild(link);
 link.click();
 document.body.removeChild(link);

このコードは、エンコードされたデータをリンクに配置し、リンクのクリックをシミュレートしてから削除します。


タグを非表示にして、hrefを動的に入力するだけです。追加および削除する必要はありません
BPeela

0

あなたのニーズはカバーされ window.location('download.php');
ていますが、ダウンロードするファイルを渡す必要があると思います。常に同じファイルをダウンロードするわけではありません。そのため、リクエストを使用している理由の1つは、showfile.phpのように単純なphpファイルを作成することです。のような要求を行います

var myfile = filetodownload.txt
var url = "shofile.php?file=" + myfile ;
ajaxRequest.open("GET", url, true);

showfile.php

<?php
$file = $_GET["file"] 
echo $file;

ここで、fileは、GetまたはPostを介してリクエストで渡されたファイル名であり、単純に関数で応答をキャッチします

if(ajaxRequest.readyState == 4){
                        var file = ajaxRequest.responseText;
                        window.location = 'downfile.php?file=' + file;  
                    }
                }

0

ajaxでWebページをダウンロードする別のソリューションがあります。ただし、最初に処理してからダウンロードする必要があるページを指します。

最初に、ページの処理を結果のダウンロードから分離する必要があります。

1)ajax呼び出しではページ計算のみが行われます。

$ .post( "CalculusPage.php"、{calculusFunction:true、ID:29、data1: "a"、data2: "b"}、

       関数(データ、ステータス) 
       {
            if(status == "成功") 
            {
                / * 2)回答では、以前の計算を使用するページがダウンロードされます。たとえば、これはajax呼び出しで計算されたテーブルの結果を印刷するページである場合があります。* /
                window.location.href = DownloadPage.php + "?ID =" + 29;
            }               
       }
);

//例:CalculusPage.php

    if(!empty($ _ POST ["calculusFunction"])) 
    {
        $ ID = $ _POST ["ID"];

        $ query = "INSERT INTO ExamplePage(data1、data2)VALUES( '"。$ _ POST ["data1"]。 "'、 '"。$ _ POST ["data2"]。 "')WHERE id ="。$ ID;
        ...
    }

//例:DownloadPage.php内

    $ ID = $ _GET ["ID"];

    $ sede = "SELECT * FROM ExamplePage WHERE id ="。$ ID;
    ...

    $ filename = "Export_Data.xls";
    header( "Content-Type:application / vnd.ms-excel");
    header( "Content-Disposition:inline; filename = $ filename");

    ...

このソリューションが多くの人にとって役立つことを願っています。


0

よりモダンなアプローチをお探しの方は、を使用できますfetch API。次の例は、スプレッドシートファイルをダウンロードする方法を示しています。以下のコードで簡単にできます。

fetch(url, {
    body: JSON.stringify(data),
    method: 'POST',
    headers: {
        'Content-Type': 'application/json; charset=utf-8'
    },
})
.then(response => response.blob())
.then(response => {
    const blob = new Blob([response], {type: 'application/application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'});
    const downloadUrl = URL.createObjectURL(blob);
    const a = document.createElement("a");
    a.href = downloadUrl;
    a.download = "file.xlsx";
    document.body.appendChild(a);
    a.click();
})

このアプローチは他のXMLHttpRequestソリューションよりもはるかに理解しやすいと思います。また、これはjQueryアプローチに似た構文を持ち、追加のライブラリを追加する必要はありません。

もちろん、この新しいアプローチはIEでは機能しないため、開発中のブラウザーを確認することをお勧めします。完全なブラウザ互換性リストは、次のリンクにあります。

重要:この例では、指定されたをリッスンしているサーバーにJSONリクエストを送信していますurl。これurlは設定する必要があります。私の例では、この部分を知っていると想定しています。また、リクエストが機能するために必要なヘッダーも検討してください。私はJSONを送信しているので、Content-Typeヘッダーを追加してに設定する必要application/json; charset=utf-8があります。これにより、サーバーが受信する要求のタイプをサーバーに通知します。


0

@Joao Marcosソリューションは私にとっては機能しますが、IEで機能するようにコードを変更する必要がありました。

       downloadFile(url,filename) {
        var that = this;
        const extension =  url.split('/').pop().split('?')[0].split('.').pop();

        var req = new XMLHttpRequest();
        req.open("GET", url, true);
        req.responseType = "blob";
        req.onload = function (event) {
            const fileName = `${filename}.${extension}`;
            const blob = req.response;

            if (window.navigator.msSaveBlob) { // IE
                window.navigator.msSaveOrOpenBlob(blob, fileName);
            } 
            const link = document.createElement('a');
            link.href = window.URL.createObjectURL(blob);                
            link.download = fileName;
            link.click();
            URL.revokeObjectURL(link.href);

        };

        req.send();
    },
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.