WebSurfer's Home

トップ > Blog 1   |   ログイン
APMLフィルター

Ajax でファイルダウンロード

by WebSurfer 2019年2月5日 18:38

Ajax を使ってファイルをダウンロードするにはどうすれば良いかということを調べましたので備忘録として書いておきます。

Ajax でファイルダウンロード

例えば、先の記事「MVC でファイルのダウンロード」で書いたようなアクションメソッドがあるとして、Ajax を使ってその URL に非同期要求をかけたとします。

そうすると、同期要求した場合と全く同様に、応答ヘッダには Content-Type と Content-Disposition が適切に設定され、コンテンツ(ファイルのバイナリデータ)も正しく返ってきます。

しかしながら、上の画像のような通知バーは表示されませんし、ファイルは PC のディスクには保存されません。

では、ファイルを PC のディスクに保存するにはどうすればいいかと言うと、応答コンテンツを Blob(生データ)として取得し、それを保存するためのスクリプトを書くことになります。

jQuery ajax を使う場合、バージョン 3.0 以降であれば Blob を取得できますが、バージョン 2.x 以前では Blob を取得できないので、ネイティブの XMLHttpRequest を使うことになるそうです (fetch API が使えればそちらを使う方がお勧め)。

詳しくは stackoverflow の記事 Using jQuery's ajax method to retrieve images as a blob にある回答を読んでください。

取得した Blob をファイルとして PC に保存するには、IE, 旧 Edge の場合は msSaveBlob method を使います。

Chrome, Firefox, Opera の場合は URL.createObjectURL を使って Blob の URL を取得し、それを html の a 要素の href 属性に設定し、download 属性にファイル名を設定してスクリプトでクリックするようにします。

具体的には、ネイティブの XMLHttpRequest を使った例ですが、以下のコードの通りです。(下の方に jQuery Ajax と fetch API を利用したサンプルコードを追記しました)

function download() {
  var url = "/Home/FileDownload";
  var filename = "testfile";

  var xhr = new XMLHttpRequest();
  xhr.open('GET', url, true);
  xhr.responseType = 'blob';
  xhr.onreadystatechange = function (e) {
    if (this.readyState == 4 && this.status == 200) {
      var blob = this.response;

      //IE, 旧 Edge とその他で処理の切り分け
      if (window.navigator.msSaveBlob) {
        window.navigator.msSaveBlob(blob, filename + ".pdf");
      } else {
        var a = document.createElement("a");
        // IE11, 旧 Edge  は URL API をサポートしてない
        var url = window.URL;
        a.href = url.createObjectURL(new Blob([blob],
                                     { type: blob.type }));
        document.body.appendChild(a);
        a.style = "display: none";
        a.download = filename + ".pdf";
        a.click();
      }
    }
  };
  xhr.send();
}

2, 3 注意点を書いておきます。

IE11 は window.URL をサポートしてないのでファイルとして保存するには msSaveBlob メソッドを使う以外に手はなさそうです。

msSaveBlob の第 2 引数を設定しないと、ファイル名は IE の場合 1A31A31A-1F57-4D8D-8C70-150839D02536.pdf のように、Edge の場合は (1) となってしまいます。

MDN のドキュメントには、Content-Disposition ヘッダーで download 属性の指定と異なるファイル名が与えられた場合は、この属性より HTTP ヘッダーが優先するということが書いてありますが、実際試すと download 属性の指定通りとなります。"" を設定したりすると aae9adeb-1005-407f-a3a6-046fa79c4351.pdf のようになります。


【追記 2023/12/6】

jQuery Ajax を利用

上に紹介した stackoverflow の記事によると jQuery バージョン 3.0 以降であれば Blob を取得できるそうで、サンプルコードも載っていましたので試してみました。以下のコードでダウンロードできます。

const jqueryAjax = () => {
    const url = "/Download/VirtualFileResult";
    const filename = "testjquery";

    $.ajax({
        url: url,
        cache: false,
        xhrFields: { responseType: "blob" }
    }).done(blob => {
        const a = document.createElement("a");
        const url = window.URL;
        a.href = url.createObjectURL(new Blob([blob],
            { type: blob.type }));
        document.body.appendChild(a);
        a.style = "display: none";
        a.download = filename + ".pdf";
        a.click();
    }).fail((jqXHR, textStatus, errorThrown) => {
        // エラー処理(省略)
    });
};

fetch API を利用

fetch 関数の戻り値の Response オブジェクトの blob メソッドで blob を取得できるそうなので試してみました。以下のコードがそれです。async / await と組み合わせることにより、上の 2 つの例より、可読性がかなり向上すると思います。

const fetchApi = async () => {
    const url = "/Download/VirtualFileResult";
    const filename = "testfetch";

    const response = await fetch(url);
    if (response.ok) {
        const blob = await response.blob();
        const a = document.createElement("a");
        const url = window.URL;
        a.href = url.createObjectURL(new Blob([blob],
            { type: blob.type }));
        document.body.appendChild(a);
        a.style = "display: none";
        a.download = filename + ".pdf";
        a.click();
    } else {
        // エラー処理(省略)
    }
};

(メモ: サンプルは Visual Studio 2022 > AspNet7 > McvNet7App > DownloadController)

Tags: ,

Upload Download

About this blog

2010年5月にこのブログを立ち上げました。主に ASP.NET Web アプリ関係の記事です。

Calendar

<<  2024年4月  >>
31123456
78910111213
14151617181920
21222324252627
2829301234
567891011

View posts in large calendar