ダウンロードといえば本来サーバ側に実装する機能だが、超短納期で片づけたかったため時間のかからなそうな、javascript側のStoreからダウンロードする方法を検討した。
作成したサンプルは以下の2つ。いずれもExtJSのサンプルに、ダウンロード機能を呼び出すためのボタンを追加したものとなる。
Chartのほうはサーバに無いデータを表示する例なので、よりStoreからのダウンロードに意味が出るかと思う。
ダウンロードはクライアント側だけでは仕組み上実現できないので、ブラウザからPOSTされたデータをそのまま出力する鏡みたいなphpスクリプトを使用することにした。このあたり、javascriptだけでできる方法があるなら、是非コメントでアドバイスいただきたい。
用意したphpスクリプトdownload.phpは以下のようなものだ。
本来、3行で実装できるが、ヘッダの自由度を持たせるためと、最低限のセキュリティとして、リファラが同じサーバかだけのチェックを入れている。
<?php if (!isSet($_SERVER['HTTP_REFERER']) || !preg_match('#^http://([^:/]+)#',$_SERVER['HTTP_REFERER'],$mch) || $mch[1]!==$_SERVER['SERVER_NAME'] ) die('Security Error'); // simple security check foreach ($_POST as $header=>$val) { if ($header==='body') continue; header("$header: $val"); } die($_POST['body']); ?>
ChartやGridPanelのスクリプトの変更方法は、ツールバーやツールボタンで、下記のStoreに追加したメソッドExt.data.Store::download(fn)を呼び出すというもの、StoreはGridやChartで使用しているものを、引数fnはダウンロードの際のファイル名を指定すればよい。
Ext.data.Store.prototype.download = function(fn){ var output=[],o=[], fld = this.fields; fld.eachKey(function(key){o.push(key)}); output.push(o.join('\t')); this.each(function(rec) { var o=[]; fld.eachKey(function(key){o.push(rec.get(key))}); output.push(o.join('\t')); }); var form = Ext.DomHelper.append(document.body, { tag: 'form', style: 'display:none', action: 'download.php', method: 'post', cn:[{ tag:'textarea', name:'body', html:output.join('\n') },{ tag:'input', name:'Content-Disposition', value:'attachment;filename='+'"'+fn+'"' },{ tag:'input', name:'Content-Type', value:'text/tab-separated-values' }] }); form.submit(); document.body.removeChild(form); }
今回の方式はStoreにfilterがかかっていれば、表示されているレコードのみDLされる。
Grid専用にするならば、これを一歩進め、ユーザが画面で操作した列の順番や列の表示/非表示を反映したり、列によってはrendererを指定できるようにすると、ユーザが見たままを手に入れるという意味で完成形になるのではと思う。
今回の例だと出力したLastUpdateの列のDateの出力形式は、欧米人がみればわかりやすいが、日本人向けでなく、Excelでそのまま処理できない等の不満はある。
英語版のフォーラムに投稿してみました
http://www.sencha.com/forum/showthread.php?125611-data-download-function-from-Grid-and-Chart
その後、「ExtJSのStoreにデータダウンロードの機能を追加する PART2」 にて、ダウンロードファイルの列や、データのフォーマットを指定できるようにしました
返信削除