jQuery.Ajaxでファイルをダウンロードする


420

サーバー側でファイルをダウンロードするためのStruts2アクションがあります。

<action name="download" class="com.xxx.DownAction">
    <result name="success" type="stream">
        <param name="contentType">text/plain</param>
        <param name="inputName">imageStream</param>
        <param name="contentDisposition">attachment;filename={fileName}</param>
        <param name="bufferSize">1024</param>
    </result>
</action>

ただし、jQueryを使用してアクションを呼び出すと:

$.post(
  "/download.action",{
    para1:value1,
    para2:value2
    ....
  },function(data){
      console.info(data);
   }
);

Firebugでは、データがBinaryストリームで取得されているのがわかります。ユーザーがファイルをローカルに保存できるファイルダウンロードウィンドウを開く方法を教えてください。



1
プラットフォームの違いにもかかわらず、私はそれを複製としてマークしました。なぜなら、私が見る限り、ソリューションは同じです(Ajaxでこれを行うことはできず、行う必要もないため)。
Pekka

1
したがって、ajaxなしで、window.location = "download.action?para1 = value1 ...."?
hguser

回答:


676

2019の最新ブラウザの更新

これは、私が現在推奨するアプローチですが、いくつかの注意点があります。

  • 比較的新しいブラウザが必要です
  • ファイルが非常に大きいことが予想される場合、以下の操作の一部が少なくともダウンロード中のファイルや他の興味深いCPUと同じ大きさのシステムメモリを消費する可能性があるため、元のアプローチ(iframeおよびcookie)と同様のことを行う必要があります副作用。

fetch('https://jsonplaceholder.typicode.com/todos/1')
  .then(resp => resp.blob())
  .then(blob => {
    const url = window.URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.style.display = 'none';
    a.href = url;
    // the filename you want
    a.download = 'todo-1.json';
    document.body.appendChild(a);
    a.click();
    window.URL.revokeObjectURL(url);
    alert('your file has downloaded!'); // or you know, something with better UX...
  })
  .catch(() => alert('oh no!'));

2012オリジナルのjQuery / iframe / Cookieベースのアプローチ

Bluishはこれについて完全に正しいです。JavaScriptはユーザーのコンピューターに直接ファイルを保存できないため(セキュリティ上の理由から)、Ajaxを使用してそれを行うことはできません。残念ながら、メインウィンドウの URLをファイルのダウンロードに向けると、ファイルのダウンロードが発生したときのユーザーエクスペリエンスをほとんど制御できなくなります。

OnSuccessコールバックとOnFailureコールバックを備えたファイルダウンロードで "Ajaxのような"エクスペリエンスを可能にするjQueryファイルダウンロードを作成して、ユーザーエクスペリエンスを向上させました。プラグインが解決する一般的な問題と、プラグインを使用するいくつかの方法、および実際のjQueryファイルダウンロードのデモに関する私のブログ投稿をご覧ください。ここにソースがあります

ここに、promise 付きのプラグインソースを使用した簡単な使用例のデモがあります。デモページには、他の多くの、「より良いUX」の例を含んでいます。

$.fileDownload('some/file.pdf')
    .done(function () { alert('File download a success!'); })
    .fail(function () { alert('File download failed!'); });

サポートする必要のあるブラウザーによっては、https://github.com/eligrey/FileSaver.js/を使用できる場合がありますこれにより、jQueryファイルのダウンロードで使用するIFRAMEメソッドよりも明示的な制御が可能になります。


69
私はあなたが作ったものを愛していますが、StackOverFlowクレジットを増やすには、ここでの回答にもう少し詳細を含める必要があると思います。具体的には、問題をどのように解決したかについて。
AnthonyVO

14
この「プラグイン」が制限を回避する方法を正確に説明して、ブログ/プラグインのソースにアクセスしてそれを表示するように強制するのではなく、それがいいでしょう。たとえば、代わりにiframeに投稿していますか?代わりに、ファイルを保存してURLを返すためにリモートスクリプトが必要ですか?
ケビンB

2
@asgerhallasもちろんですが、上記のリンクが消えた場合、それはまったく役に立ちません。
Kevin B

26
私は同意します。ブログは、プラグインの使用方法と機能の詳細な説明を掲載するのにはるかに適しています。しかし、少なくともこのプラグインが問題を解決する方法の短い概要を与えることができたでしょう。たとえば、サーバーにCookieを設定させ、JavaScriptが存在するまでCookieを継続的に検索させることで、問題を解決します。存在する場合、ダウンロードが完了したと想定できます。この種の情報があれば、簡単に独自のソリューションをすばやく導入することができ、答えはブログ/プラグイン/ jqueryに100%依存することはなくなり、他のライブラリに適用できます。
Kevin B

1
Royi、私が理解しているように、AJAXはファイルダウンロードポップアップをディスクに保存するファイルダウンロードをサポートすることできません。私が知らない方法を見つけましたか?
John Culviner 2014年

227

誰もこの@Pekkaのソリューションを投稿していません...投稿します。それは誰かを助けることができます。

Ajaxでこれを行う必要はありません。使うだけ

window.location="download.action?para1=value1...."

4
いいですね...ダウンロードファイルプロンプトの処理とjquery ajaxの使用に苦労していたので、このソリューションは完璧に機能します.. + 1
swapnesh

45
これには、サーバーがContent-Dispositionヘッダー値を 'attachment'に設定する必要があることに注意してください。それ以外の場合、ブラウザーは応答コンテンツにリダイレクト(および表示)します
brichins

21
またはwindow.open(<url>, '_blank');、ダウンロードが現在のブラウザコンテンツを置き換えないようにするために使用します(Content-Dispositionヘッダーに関係なく)。
クリストファーキング

4
このソリューションの問題は、操作が失敗した場合やサーバーがエラーを返した場合に、ページがエラーページにリダイレクトされることです。それを解決するには、iFrameソリューションを使用してください
kofifus 2015年

4
このソリューションの本当の問題-質問はPOSTリクエストについてです。
アトモスク

35

あなたはHTML5でできます

注:バイナリデータをJSONエンコードできないため、返されるファイルデータはbase64でエンコードする必要があります

私のAJAX応答には、次のようなデータ構造があります。

{
    result: 'OK',
    download: {
        mimetype: string(mimetype in the form 'major/minor'),
        filename: string(the name of the file to download),
        data: base64(the binary data as base64 to download)
    }
}

つまり、私はAJAX経由でファイルを保存するために次のことを行うことができます

var a = document.createElement('a');
if (window.URL && window.Blob && ('download' in a) && window.atob) {
    // Do it the HTML5 compliant way
    var blob = base64ToBlob(result.download.data, result.download.mimetype);
    var url = window.URL.createObjectURL(blob);
    a.href = url;
    a.download = result.download.filename;
    a.click();
    window.URL.revokeObjectURL(url);
}

関数base64ToBlobはここから取得されており、この関数に従って使用する必要があります

function base64ToBlob(base64, mimetype, slicesize) {
    if (!window.atob || !window.Uint8Array) {
        // The current browser doesn't have the atob function. Cannot continue
        return null;
    }
    mimetype = mimetype || '';
    slicesize = slicesize || 512;
    var bytechars = atob(base64);
    var bytearrays = [];
    for (var offset = 0; offset < bytechars.length; offset += slicesize) {
        var slice = bytechars.slice(offset, offset + slicesize);
        var bytenums = new Array(slice.length);
        for (var i = 0; i < slice.length; i++) {
            bytenums[i] = slice.charCodeAt(i);
        }
        var bytearray = new Uint8Array(bytenums);
        bytearrays[bytearrays.length] = bytearray;
    }
    return new Blob(bytearrays, {type: mimetype});
};

これは、サーバーが保存するファイルデータをダンプしている場合に適しています。ただし、HTML4フォールバックをどのように実装するかはまだ十分に検討していません


1
a.click()Firefoxで動作するようには思えない...任意のアイデアを?
bigpony

あなたが追加する必要があるかもしれません一部のブラウザでaの作業には、このコードの順にDOMにおよび/または削除revokeObjectURL:一部をdocument.body.appendChild(a)
bigpony

私の日を救った(そしておそらく仕事も:) ただし、単純な「createObjectURL(new Blob([atob(base64)]))」が機能しない理由はわかりません!すべての本能はそれをする必要があると言いながら、それは単にしません。grrr ...
apil.tamang

var bytechars = atob(base64)でエラーをスローしますJavaScript runtime error: InvalidCharacterError。Chromeバージョン75.0.3770.142を使用していますが、ここで何が問題なのかわかりません。
Muflix

27

1.フレームワークにとらわれない:添付ファイルとしてのサーブレットダウンロードファイル

<!-- with JS -->
<a href="javascript:window.location='downloadServlet?param1=value1'">
    download
</a>

<!-- without JS -->
<a href="downloadServlet?param1=value1" >download</a>

2. Struts2 Framework:添付ファイルとしてファイルをダウンロードするアクション

<!-- with JS -->
<a href="javascript:window.location='downloadAction.action?param1=value1'">
    download
</a>

<!-- without JS -->
<a href="downloadAction.action?param1=value1" >download</a>

タグで作成されたURL<s:a>OGNLで指すタグを使用することをお勧めます<s:url>

<!-- without JS, with Struts tags: THE RIGHT WAY -->    
<s:url action="downloadAction.action" var="url">
    <s:param name="param1">value1</s:param>
</s:ulr>
<s:a href="%{url}" >download</s:a>

上記の例では、あなたが必要とする書き込みにコンテンツディスポジションにヘッダを応答ファイル(ダウンロードする必要があることを指定し、attachment()およびブラウザで開かれていませんinline)。コンテンツタイプも指定する必要があり、ファイル名と長さを追加することもできます(ブラウザが現実的なプログレスバーを描画するのを助けるため)。

たとえば、ZIPをダウンロードする場合:

response.setContentType("application/zip");
response.addHeader("Content-Disposition", 
                   "attachment; filename=\"name of my file.zip\"");
response.setHeader("Content-Length", myFile.length()); // or myByte[].length...

Struts2を使用する場合(たとえば、アクションをサーブレットとして使用している場合を除き、ダイレクトストリーミングのためのハックなど)、応答に直接何かを書き込む必要はありません。単に使用してストリームの結果の型を動作し、struts.xmlでそれを設定:

<result name="success" type="stream">
   <param name="contentType">application/zip</param>
   <param name="contentDisposition">attachment;filename="${fileName}"</param>
   <param name="contentLength">${fileLength}</param>
</result>

3.フレームワークにとらわれない(/ Struts2フレームワーク):ブラウザー内でサーブレット(/アクション)オープンファイル

ファイルをダウンロードするのではなく、ブラウザー内で開く場合は、Content-dispositioninlineに設定する必要がありますが、ターゲットを現在のウィンドウの場所にすることはできません。JavaScriptで作成された新しいウィンドウ<iframe>、ページ内、または「ディスカッションされた」target = "_ blank"を使用してオンザフライで作成された新しいウィンドウをターゲットにする必要があります。

<!-- From a parent page into an IFrame without javascript -->   
<a href="downloadServlet?param1=value1" target="iFrameName">
    download
</a>

<!-- In a new window without javascript --> 
<a href="downloadServlet?param1=value1" target="_blank">
    download
</a>

<!-- In a new window with javascript -->    
<a href="javascript:window.open('downloadServlet?param1=value1');" >
    download
</a>

2
サー、あなたの入力: "Content-Disposition"、 "inline; ....貧しいコーダーの日を救いました:)
Vedran Maricevic。'29年

1
これは、「window.open」について言及する唯一の回答です(コメントの1つで言及されています)。
Andrew Koster

too long urlエラーが発生するため、パラメータが多い場合は機能しません。
Muflix

25

ブラウザにファイルをダウンロードさせる簡単な方法は、次のようなリクエストを行うことです。

 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();
 }

これにより、ブラウザのダウンロードポップアップが開きます。


3
ありがとう、私はこの解決策を使いました。魅力のように働いた。また、応答からBLOBを取得できない場合は、新しいBLOBを作成します。
fabio.sang 2017年


@startsWith_Rからのリンクは、IE11を使用している場合に非常に役立ちます
alexventuraio

ありがとうございます。
Zaki Mohammed

23

私は回避策として小さな関数を作成しました(@JohnCulvinerプラグインに触発されました):

// creates iframe and form in it with hidden field,
// then submit form with provided data
// url - form url
// data - data to form field
// input_name - form hidden input name

function ajax_download(url, data, input_name) {
    var $iframe,
        iframe_doc,
        iframe_html;

    if (($iframe = $('#download_iframe')).length === 0) {
        $iframe = $("<iframe id='download_iframe'" +
                    " style='display: none' src='about:blank'></iframe>"
                   ).appendTo("body");
    }

    iframe_doc = $iframe[0].contentWindow || $iframe[0].contentDocument;
    if (iframe_doc.document) {
        iframe_doc = iframe_doc.document;
    }

    iframe_html = "<html><head></head><body><form method='POST' action='" +
                  url +"'>" +
                  "<input type=hidden name='" + input_name + "' value='" +
                  JSON.stringify(data) +"'/></form>" +
                  "</body></html>";

    iframe_doc.open();
    iframe_doc.write(iframe_html);
    $(iframe_doc).find('form').submit();
}

クリックイベントのデモ:

$('#someid').on('click', function() {
    ajax_download('/download.action', {'para1': 1, 'para2': 2}, 'dataname');
});

ただし、非常に奇妙な方法でデータがサーバーに送信されます。準拠したPOSTを作成するように変更できるのでしょうか?
Shayne

16

私は同じ問題に直面し、それを首尾よく解決しました。私のユースケースはこれです。

" サーバーにJSONデータを投稿してExcelファイルを受信します。そのExcelファイルはサーバーによって作成され、クライアントへの応答として返されます。その応答をカスタム名のファイルとしてブラウザーにダウンロードしてください "

$("#my-button").on("click", function(){

// Data to post
data = {
    ids: [1, 2, 3, 4, 5]
};

// Use XMLHttpRequest instead of Jquery $ajax
xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
    var a;
    if (xhttp.readyState === 4 && xhttp.status === 200) {
        // Trick for making downloadable link
        a = document.createElement('a');
        a.href = window.URL.createObjectURL(xhttp.response);
        // Give filename you wish to download
        a.download = "test-file.xls";
        a.style.display = 'none';
        document.body.appendChild(a);
        a.click();
    }
};
// Post data to URL which handles post request
xhttp.open("POST", excelDownloadUrl);
xhttp.setRequestHeader("Content-Type", "application/json");
// You should set responseType as blob for binary responses
xhttp.responseType = 'blob';
xhttp.send(JSON.stringify(data));
});

上記のスニペットは次のようにしています

  • XMLHttpRequestを使用してサーバーに配列をJSONとして投稿する。
  • コンテンツをblob(バイナリ)としてフェッチした後、ダウンロード可能なURLを作成し、それを非表示の「a」リンクに添付してクリックします。ここでPOSTリクエストを行いました。代わりに、単純なGETも実行できます。Ajax経由でファイルをダウンロードすることはできません。XMLHttpRequestを使用する必要があります。

ここでは、サーバー側でいくつかの設定を慎重に行う必要があります。Python Django HttpResponseにいくつかのヘッダーを設定しました。他のプログラミング言語を使用する場合は、それに応じて設定する必要があります。

# In python django code
response = HttpResponse(file_content, content_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")

ここでxls(excel)をダウンロードしたので、contentTypeを上記の1に調整しました。ファイルタイプに応じて設定する必要があります。この手法を使用して、あらゆる種類のファイルをダウンロードできます。


「Ajax経由でファイルをダウンロードすることはできません。XMLHttpRequestを使用する必要があります。」XMLHttpRequestは定義上AJAXです。それ以外の場合は、最新のWebブラウザーに最適なソリューションです。をサポートしていないIEの場合HTMLAnchorElement.download、独自のmsSaveOrOpenBlobメソッドと組み合わせることを考えています。
Tsahi Asher、2018

15

わかりました、ndpuのコードに基づいてajax_downloadの改良(私は思う)バージョンです-

function ajax_download(url, data) {
    var $iframe,
        iframe_doc,
        iframe_html;

    if (($iframe = $('#download_iframe')).length === 0) {
        $iframe = $("<iframe id='download_iframe'" +
                    " style='display: none' src='about:blank'></iframe>"
                   ).appendTo("body");
    }

    iframe_doc = $iframe[0].contentWindow || $iframe[0].contentDocument;
    if (iframe_doc.document) {
        iframe_doc = iframe_doc.document;
    }

    iframe_html = "<html><head></head><body><form method='POST' action='" +
                  url +"'>" 

    Object.keys(data).forEach(function(key){
        iframe_html += "<input type='hidden' name='"+key+"' value='"+data[key]+"'>";

    });

        iframe_html +="</form></body></html>";

    iframe_doc.open();
    iframe_doc.write(iframe_html);
    $(iframe_doc).find('form').submit();
}

これをこのように使用します;-

$('#someid').on('click', function() {
    ajax_download('/download.action', {'para1': 1, 'para2': 2});
});

paramsは、前の例のようにjsonエンコードされた文字列としてではなく、入力から来るかのように適切なpost paramsとして送信されます。

警告:これらのフォームでの可変インジェクションの可能性に注意してください。これらの変数をエンコードするより安全な方法があるかもしれません。あるいは、それらをエスケープすることを検討してください。


これは実際の例です。ありがとう。iframeなしでwindow.locationなしでそれを行うことは可能ですか?
Marek Bar

非表示のフォームをDOMの下部に追加するだけでよいと思います。Shadow domの使用も検討する価値があるかもしれませんが、それは古いブラウザでは必ずしも十分にサポートされているわけではありません。
Shayne、2014年

このコードでは、このエラーが発生しています。Uncaught SecurityError: Blocked a frame with origin "http://foo.bar.com" from accessing a frame with origin "null". The frame requesting access has a protocol of "http", the frame being accessed has a protocol of "data". Protocols must match.
無効

このフォームをモデルクラスにマッピングするにはどうすればよいですか?:私が持っている @ResourceMapping() public void downloadFile(final ResourceRequest request, final ResourceResponse response, @ModelAttribute("downForm") FormModel model) ..しかし、それは働いていない
bartex9

void:ある種のクロスオリジンセキュリティの問題である可能性があります。それはおそらくそれ自体でスタック全体のオーバーフローの質問です。@ bartex9:使用するフレームワークの種類に大きく依存します。しかし、原則は、名前とパスを取得してそれを保存し、ファイル自体をファイルシステムのWebアクセス可能領域にプッシュするか、高可用性のためにAmazon S3のようなものに
Shayne

8

これが私がやったこと、純粋なjavascriptとhtmlです。テストしませんでしたが、これはすべてのブラウザで機能するはずです。

JavaScript関数

var iframe = document.createElement('iframe');
iframe.id = "IFRAMEID";
iframe.style.display = 'none';
document.body.appendChild(iframe);
iframe.src = 'SERVERURL'+'?' + $.param($scope.filtro);
iframe.addEventListener("load", function () {
     console.log("FILE LOAD DONE.. Download should start now");
});

すべてのブラウザでサポートされているコンポーネントのみを使用し、追加のライブラリはありません。

ここに画像の説明を入力してください ここに画像の説明を入力してください

これが私のサーバーサイドのJAVA Springコントローラーコードです。

@RequestMapping(value = "/rootto/my/xlsx", method = RequestMethod.GET)
public void downloadExcelFile(@RequestParam(value = "param1", required = false) String param1,
    HttpServletRequest request, HttpServletResponse response)
            throws ParseException {

    Workbook wb = service.getWorkbook(param1);
    if (wb != null) {
        try {
            String fileName = "myfile_" + sdf.format(new Date());
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            response.setHeader("Content-disposition", "attachment; filename=\"" + fileName + ".xlsx\"");
            wb.write(response.getOutputStream());
            response.getOutputStream().close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    }

Content-Disposition添付コンテンツに対してロードイベントが呼び出されていないようです(iframeに何もロードされていないため)。これが機能する場合(console.logが表示されます)pls post a sample
kofifus

ここに簡単なフィドルjsfiddle.net/y2xezyojがあります。これは、pdfファイルがiframeにロードされるとすぐにロードイベントを起動します。ダウンロード用のキーがサーバー側にあるため、このフィドルはダウンロードされません "response.setHeader(" Content -disposition "、" 添付ファイル ;ファイル名= \ "" + fileName + ".xlsx \" ");"
manukyanv07 2015年

1
はい、その場合は機能しますが、ファイルがダウンロードされた場合、つまりサーバーがContent-Disposition:添付ファイルを送信すると、loadイベントは発生しません。これは私のポイント
でした

サーバーが完了するとすぐにロードイベントが発生します。処理はファイルの送信を開始します。これは私が探していたもので、1-ボタンをブロックして処理を表示し、ユーザーが物事が起こっているというフィードバックを持つことができるようにします。2-次に、サーバーが処理を完了してファイルを送信しようとすると、3-(loadイベントが発生します)ボタンがロック解除され、処理スピナーが削除されます4-ユーザーがファイルを保存するか、ブラウザがダウンロードを開始します定義されたダウンロード場所。すみません、私の英語。
manukyanv07 2015

5
function downloadURI(uri, name) 
{
    var link = document.createElement("a");
    link.download = name;
    link.href = uri;
    link.click();
}

あなたの答えを説明してくれませんか?これは、他の人があなたがやったことを理解して、あなたのテクニックを彼らの状況に適用できるようにするのに役立ちます。
Wai Ha Lee

2
ただの警告:SafariとIEはこのdownload属性をサポートしていないため、ファイル名は「不明」になります
Yangshun Tay

4

Railsでは、次のようにします。

function download_file(file_id) {
  let url       = '/files/' + file_id + '/download_file';
    $.ajax({
    type: 'GET',
    url: url,
    processData: false,
    success: function (data) {
       window.location = url;
    },
    error: function (xhr) {
     console.log(' Error:  >>>> ' + JSON.stringify(xhr));
    }
   });
 }

トリックはwindow.location部分です。コントローラのメソッドは次のようになります。

# GET /files/{:id}/download_file/
def download_file
    send_file(@file.file,
          :disposition => 'attachment',
          :url_based_filename => false)
end

2
簡単な質問ですが、これによりファイルが2回生成されませんか?ajaxリクエストを送信したら。次に、ページを同じURLにリダイレクトします。どうすればそれを排除できますか?
コーダー

私の場合ではありません。ただし、Chromeでのみテストしました。
aarkerio

コーダーはすでに正しく述べているため、アクションは2回呼び出されます。
2018年

私にも2回呼び出されます。
CSquared

4

https://developer.mozilla.org/en-US/docs/Web/API/Window/openを使用してwindow.open ください

たとえば、次のコード行をクリックハンドラーに配置できます。

window.open('/file.txt', '_blank');

(「_blank」ウィンドウ名のため)新しいタブが開き、そのタブでURLが開きます。

サーバー側のコードも次のようになります。

res.set('Content-Disposition', 'attachment; filename=file.txt');

そしてそのようにして、ブラウザはユーザーにファイルを表示するだけでなく、ファイルをディスクに保存するようユーザーに促すべきです。開いたばかりのタブも自動的に閉じます。


4

CSVファイルをダウンロードして、ダウンロードが完了した後で何かをしようとしています。したがって、適切なcallback関数を実装する必要があります。

window.location="..."ダウンロード終了後、プログラムが操作できなくなってしまうので、使用はお勧めできません。このような何か、それは良い考えではないのでヘッダーを変更します。

fetchは良い代替手段ですが、IE 11をサポートできません。そして、window.URL.createObjectURLIE 11.Youを参照することができますサポートすることはできません、これを

これは私のコードで、Shahrukh Alamのコードに似ています。ただし、window.URL.createObjectURLメモリリークが発生する可能性があることに注意してください。あなたは、参照することができ、これを。応答が到着すると、データはブラウザのメモリに保存されます。aリンクをクリックする前に、ファイルがダウンロードされています。ダウンロード後は何でもできるということです。

$.ajax({
    url: 'your download url',
    type: 'GET',
}).done(function (data, textStatus, request) {
    // csv => Blob
    var blob = new Blob([data]);

    // the file name from server.
    var fileName = request.getResponseHeader('fileName');

    if (window.navigator && window.navigator.msSaveOrOpenBlob) { // for IE
    window.navigator.msSaveOrOpenBlob(blob, fileName);
    } else { // for others
    var url = window.URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.style.display = 'none';
    a.href = url;
    a.download = fileName;
    document.body.appendChild(a);
    a.click();
    window.URL.revokeObjectURL(url);

    //Do something after download 
    ...

    }
}).then(after_download)
}

4

AJAXで受信したファイルをダウンロードする方法

ファイルが長期間作成され、PRELOADERを表示する必要がある場合に便利です。

Webフォームを送信する場合の例:

<script>
$(function () {
    $('form').submit(function () {
        $('#loader').show();
        $.ajax({
            url: $(this).attr('action'),
            data: $(this).serialize(),
            dataType: 'binary',
            xhrFields: {
                'responseType': 'blob'
            },
            success: function(data, status, xhr) {
                $('#loader').hide();
                // if(data.type.indexOf('text/html') != -1){//If instead of a file you get an error page
                //     var reader = new FileReader();
                //     reader.readAsText(data);
                //     reader.onload = function() {alert(reader.result);};
                //     return;
                // }
                var link = document.createElement('a'),
                    filename = 'file.xlsx';
                // if(xhr.getResponseHeader('Content-Disposition')){//filename 
                //     filename = xhr.getResponseHeader('Content-Disposition');
                //     filename=filename.match(/filename="(.*?)"/)[1];
                //     filename=decodeURIComponent(escape(filename));
                // }
                link.href = URL.createObjectURL(data);
                link.download = filename;
                link.click();
            }
        });
        return false;
    });
});
</script>

例を簡略化するために、オプションの機能はコメント化されています。

サーバーに一時ファイルを作成する必要はありません。

jQuery v2.2.4ではOK。古いバージョンでエラーが発生します:

Uncaught DOMException: Failed to read the 'responseText' property from 'XMLHttpRequest': The value is only accessible if the object's 'responseType' is '' or 'text' (was 'blob').

Content-Dispositionからファイル名を取得するために、次の一致が機能しましたfilename.match(/filename=(.*)/)[1](二重引用符または疑問符なし)-regex101.com/r/2AsD4y/2。しかし、あなたのソリューションは、多くの検索を行った後に機能した唯一のソリューションでした。
jstuardo

3

上記の回答にファイルをダウンロードするためのいくつかの追加事項

以下は、バイト配列を生成するいくつかのJava Springコードです

@RequestMapping(value = "/downloadReport", method = { RequestMethod.POST })
    public ResponseEntity<byte[]> downloadReport(
            @RequestBody final SomeObejct obj, HttpServletResponse response) throws Exception {

        OutputStream out = new ByteArrayOutputStream();
        // write something to output stream
        HttpHeaders respHeaders = new HttpHeaders();
        respHeaders.setContentType(MediaType.APPLICATION_OCTET_STREAM);
        respHeaders.add("X-File-Name", name);
        ByteArrayOutputStream bos = (ByteArrayOutputStream) out;
        return new ResponseEntity<byte[]>(bos.toByteArray(), respHeaders, HttpStatus.CREATED);
    }

FileSaver.jsを使用するJavaScriptコードで、以下のコードでファイルをダウンロードできます

var json=angular.toJson("somejsobject");
var url=apiEndPoint+'some url';
var xhr = new XMLHttpRequest();
//headers('X-File-Name')
xhr.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 201) {
        var res = this.response;
        var fileName=this.getResponseHeader('X-File-Name');
        var data = new Blob([res]);
        saveAs(data, fileName); //this from FileSaver.js
    }
}    
xhr.open('POST', url);
xhr.setRequestHeader('Authorization','Bearer ' + token);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.responseType = 'arraybuffer';
xhr.send(json);

上記はファイルをダウンロードします


2

わかりましたので、MVCを使用してコントローラーからファイルを取得する場合の作業コードを次に示します

あなたがあなたのバイト配列を宣言して移入させるとしましょう、あなたがする必要がある唯一のことは(System.Web.Mvcを使って)File関数を使うことです

byte[] bytes = .... insert your bytes in the array
return File(bytes, System.Net.Mime.MediaTypeNames.Application.Octet, "nameoffile.exe");

そして、同じコントローラで、これらの2つの関数を追加します

protected override void OnResultExecuting(ResultExecutingContext context)
    {
        CheckAndHandleFileResult(context);

        base.OnResultExecuting(context);
    }

    private const string FILE_DOWNLOAD_COOKIE_NAME = "fileDownload";

    /// <summary>
    /// If the current response is a FileResult (an MVC base class for files) then write a
    /// cookie to inform jquery.fileDownload that a successful file download has occured
    /// </summary>
    /// <param name="context"></param>
    private void CheckAndHandleFileResult(ResultExecutingContext context)
    {
        if (context.Result is FileResult)
            //jquery.fileDownload uses this cookie to determine that a file download has completed successfully
            Response.SetCookie(new HttpCookie(FILE_DOWNLOAD_COOKIE_NAME, "true") { Path = "/" });
        else
            //ensure that the cookie is removed in case someone did a file download without using jquery.fileDownload
            if (Request.Cookies[FILE_DOWNLOAD_COOKIE_NAME] != null)
                Response.Cookies[FILE_DOWNLOAD_COOKIE_NAME].Expires = DateTime.Now.AddYears(-1);
    }

その後、コントローラを呼び出して「成功」または「失敗」のコールバックをダウンロードして取得できます。

$.fileDownload(mvcUrl('name of the controller'), {
            httpMethod: 'POST',
            successCallback: function (url) {
            //insert success code

            },
            failCallback: function (html, url) {
            //insert fail code
            }
        });

1

実際にはajaxを使用していませんが、javascript呼び出しを使用してダウンロードをリクエストし、ダウンロードが実際に開始されたときにコールバックを取得できるという修正を見つけました。リンクがファイルを送信する前にファイルを構成するのに少し時間がかかるサーバー側スクリプトを実行する場合に、これは役に立ちました。そのため、処理中であることを警告し、最終的にファイルを送信したら、その処理通知を削除できます。ファイルがリクエストされたときにイベントが発生し、実際にダウンロードが開始されたときにイベントが発生するように、最初にajax経由でファイルをロードしようとしたのはそのためです。

フロントページのjs

function expdone()
{
    document.getElementById('exportdiv').style.display='none';
}
function expgo()
{
   document.getElementById('exportdiv').style.display='block';
   document.getElementById('exportif').src='test2.php?arguments=data';
}

iframe

<div id="exportdiv" style="display:none;">
<img src="loader.gif"><br><h1>Generating Report</h1>
<iframe id="exportif" src="" style="width: 1px;height: 1px; border:0px;"></iframe>
</div>

次に、他のファイル:

<!DOCTYPE html>
<html>
<head>
<script>
function expdone()
{
    window.parent.expdone();
}
</script>
</head>
<body>
<iframe id="exportif" src="<?php echo "http://10.192.37.211/npdtracker/exportthismonth.php?arguments=".$_GET["arguments"]; ?>"></iframe>
<script>document.getElementById('exportif').onload= expdone;</script>
</body></html>

jsを使用してデータを取得して読み取る方法があるので、phpは必要ありません。しかし、私はそれが手元にあることを知りません、そして私が使用しているサーバーはphpをサポートしているのでこれは私のために機能します。それが誰かを助ける場合に備えて私はそれを共有したいと思いました。


0

jQuery File Downloadを使用する場合は、IEでこれに注意してください。応答をリセットする必要があります。リセットしないとダウンロードされません。

    //The IE will only work if you reset response
    getServletResponse().reset();
    //The jquery.fileDownload needs a cookie be set
    getServletResponse().setHeader("Set-Cookie", "fileDownload=true; path=/");
    //Do the reset of your action create InputStream and return

アクションは実装ServletResponseAware してアクセスできますgetServletResponse()


0

Ajax呼び出しではできないことは確かです。

ただし、回避策があります。

手順:

ファイルのダウンロードにform.submit()を使用している場合、できることは次のとおりです。

  1. クライアントからサーバーへのajax呼び出しを作成し、ファイルストリームをセッション内に保存します。
  2. サーバーから「成功」が返されたら、form.submit()を呼び出して、セッションに格納されているファイルストリームをストリーミングするだけです。

これは、form.submit()の作成後にファイルをダウンロードする必要があるかどうかを判断する場合に役立ちます。例:form.submit()で、サーバー側で例外が発生し、代わりにクラッシュした場合、クライアント側でカスタムメッセージを表示する必要がある場合があります。その場合、この実装が役立つことがあります。


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
The HTML Code:-

'<button type="button" id="GetFile">Get File!</button>'


The jQuery Code:-

'$('#GetFile').on('click', function () {
    $.ajax({
        url: 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/172905/test.pdf',
        method: 'GET',
        xhrFields: {
            responseType: 'blob'
        },
        success: function (data) {
            var a = document.createElement('a');
            var url = window.URL.createObjectURL(data);
            a.href = url;
            a.download = 'myfile.pdf';
            document.body.append(a);
            a.click();
            a.remove();
            window.URL.revokeObjectURL(url);
        }
    });
});'

コードのみの回答には、コードがどのように機能し、なぜそれが質問に回答するのかを説明する最低限の説明が必要です。
Roberto Caboni

0

それはどのブラウザでもうまく機能することです(私はasp.netコアを使用しています)

            function onDownload() {

  const api = '@Url.Action("myaction", "mycontroller")'; 
  var form = new FormData(document.getElementById('form1'));

  fetch(api, { body: form, method: "POST"})
      .then(resp => resp.blob())
      .then(blob => {
          const url = window.URL.createObjectURL(blob);
        $('#linkdownload').attr('download', 'Attachement.zip');
          $('#linkdownload').attr("href", url);
          $('#linkdownload')
              .fadeIn(3000,
                  function() { });

      })
      .catch(() => alert('An error occurred'));



}
 
 <button type="button" onclick="onDownload()" class="btn btn-primary btn-sm">Click to Process Files</button>
 
 
 
 <a role="button" href="#" style="display: none" class="btn btn-sm btn-secondary" id="linkdownload">Click to download Attachments</a>
 
 
 <form asp-controller="mycontroller" asp-action="myaction" id="form1"></form>
 
 

        function onDownload() {
            const api = '@Url.Action("myaction", "mycontroller")'; 
            //form1 is your id form, and to get data content of form
            var form = new FormData(document.getElementById('form1'));

            fetch(api, { body: form, method: "POST"})
                .then(resp => resp.blob())
                .then(blob => {
                    const url = window.URL.createObjectURL(blob);
                    $('#linkdownload').attr('download', 'Attachments.zip');
                    $('#linkdownload').attr("href", url);
                    $('#linkdownload')
                        .fadeIn(3000,
                            function() {

                            });
                })
                .catch(() => alert('An error occurred'));                 

        }

-1

私はこの問題に長い間苦しんでいました。最後に、ここで提案されたエレガントな外部ライブラリが役立ちました。

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