##はじめに
サーバーで生成したExcelファイルをajaxによりダウンロードできるか調べたことをまとめたメモ。
jQueryではなく素のJavaScriptの「XMLHttpRequest()」を使用した例や、jQueryのBinaryTransportを利用して
バイナリ型のデータを取得する方法もググればすぐに出てくるのでこれらを利用してもよかったのだが、
jQueryのajaxで出来ないことに違和感を感じたのでどうせ仕事するんだから調べてみようということで調べてみた。
###動作環境
- サーバー側
- ASP.NET(VB) MVC
- OpenXmlを利用してMemoryStreamによりExcelファイルのバイナリデータを生成する
- クライアント側
- jQuery3.3.1
- IE11
##Excelファイルとして開けない例と開ける例
ググって出てきた情報から考えるとajaxのパラメータ「responseType」に「blob」が設定できれば
サーバーで生成したバイナリデータの配列を受け取れるはず・・・
しかし下記「Excelファイルとして開けない例」のようにajaxのパラメータに「responseType : "blob"」と書いてもバイナリ型のデータとして受け取ることができない。text型に変換されてしまう。
###Excelファイルとして開けない例
- サーバー側のソース
Imports DocumentFormat.OpenXml
Imports DocumentFormat.OpenXml.Packaging
Imports DocumentFormat.OpenXml.Spreadsheet
Function OutputSpreadsheet(model As FormModel) As ActionResult
Dim fileContents As Byte()
Using stream As New MemoryStream()
~~~略~~~
workbookpart.Workbook.Save()
spreadsheetDocument.Close()
fileContents = stream.ToArray()
End Using
Return File(fileContents, _
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", _
"sample.xlsx")
End Function
- クライアント側のソース
$.ajax({
type : "post",
url : "./OutputSpreadsheet",
data : FormModel,
responseType : "blob",
.done(function( data, textStatus, jqXHR ) {
var downloadData = data;
window.navigator.msSaveBlob(downloadData, "sample.xlsx");
})
.fail(function( jqXHR, textStatus, errorThrown ) {
~~~略~~~
})
.always(function( data, textStatus, errorThrown ) {
~~~略~~~
});
~~~略~~~
###Excelファイルとして開ける例
下記クライアント側のソースのようにajaxのパラメータにxhrFieldsを連想配列で指定し、要素に{responseType : 'blob'}と書いてあげればバイナリ型のデータが取得でき「window.navigator.msSaveBlob」にてExcelファイルのダウンロードができる。
「ajaxSetup」や「ajaxPrefilter」の利用および「ajaxTransport」のオーバーライドはしなくてもよい。
- サーバー側のソース
「Excelファイルとして開けない例」の「controller.vb」と同様
- クライアント側のソース
$.ajax({
type : "post",
url : "./OutputSpreadsheet",
data : FormModel,
xhrFields : {responseType : 'blob'},
.done(function( data, textStatus, jqXHR ) {
var downloadData = data;
window.navigator.msSaveBlob(downloadData, "sample.xlsx");
})
.fail(function( jqXHR, textStatus, errorThrown ) {
~~~略~~~
})
.always(function( data, textStatus, errorThrown ) {
~~~略~~~
});
~~~略~~~
##調査内容
###なぜExcelファイルが開けない?
jQuery-3.3.1.jsのソースを見ながらデバッグすると、9486行目付近の[xhr = options.xhr();]で
Excelファイルとして開けない例で示した方法で指定した「responseType」パラメータの値が空に設定される。
jQuery.ajaxTransport( function( options ) {
var callback, errorCallback;
// Cross domain only allowed if supported through XMLHttpRequest
if ( support.cors || xhrSupported && !options.crossDomain ) {
return {
send: function( headers, complete ) {
var i,
xhr = options.xhr(); <-ここでxhrの「responseType」の値が""となる
そうすると後続の9556行目付近でbinary型またはtext型の判定でtext型で返すようになる。
そのため、「window.navigator.msSaveBlob」にtext型の値を渡すことになるので破損したExcelファイルが生成される。
( xhr.responseType || "text" ) !== "text" || <-ここがfalseになるのでtext型が返る
typeof xhr.responseText !== "string" ?
{ binary: xhr.response } :
{ text: xhr.responseText },
xhr.getAllResponseHeaders()
###Excelファイルが開けるようにするには?
サーバーから返ってくる値の型が「binary」になるようにすればよいので上記9556行目付近の判定で「xhr.responseType」に何か値が入っているようにすればよい。
xhr.responseTypeの値が設定できる箇所が9500行目付近で、ajaxメソッドにてxhrFieldsパラメータを使えばxhrのプロパティ「responseType」に値を設定できる。
if ( options.xhrFields ) {
for ( i in options.xhrFields ) {
xhr[ i ] = options.xhrFields[ i ]; <-ここでresponseTypeに値を設定することができる
}
}
##参考サイト