質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

CakePHP

CakePHPは、PHPで書かれたWebアプリケーション開発用のフレームワークです。 Ruby on Railsの考え方を多く取り入れており、Railsの高速性とPHPの機動性を兼ね備えています。 MVCやORMなどを「規約優先の考え方」で利用するため、コードを書く手間を省くことができます。 外部のライブラリに依存しないので、単体での利用が可能です。

Ajax

Ajaxとは、Webブラウザ内で搭載されているJavaScriptのHTTP通信機能を使って非同期通信を利用し、インターフェイスの構築などを行う技術の総称です。XMLドキュメントを指定したURLから読み込み、画面描画やユーザの操作などと並行してサーバと非同期に通信するWebアプリケーションを実現することができます。

Q&A

1回答

4714閲覧

Cakephp3.5でPhpSpreadsheetを使ってクライアントにExcelファイルをダウンロードさせたいが上手くできません。

lovelydai

総合スコア38

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

CakePHP

CakePHPは、PHPで書かれたWebアプリケーション開発用のフレームワークです。 Ruby on Railsの考え方を多く取り入れており、Railsの高速性とPHPの機動性を兼ね備えています。 MVCやORMなどを「規約優先の考え方」で利用するため、コードを書く手間を省くことができます。 外部のライブラリに依存しないので、単体での利用が可能です。

Ajax

Ajaxとは、Webブラウザ内で搭載されているJavaScriptのHTTP通信機能を使って非同期通信を利用し、インターフェイスの構築などを行う技術の総称です。XMLドキュメントを指定したURLから読み込み、画面描画やユーザの操作などと並行してサーバと非同期に通信するWebアプリケーションを実現することができます。

0グッド

0クリップ

投稿2018/12/18 03:45

編集2018/12/18 09:09

こんにちは、cakephp3を用いて帳票出力システムを作りたいです。
帳票出力ボタンを押すと、AjaxでPHPのコントローラーに要求し、サーバからデータ処理を行い、その内容をPhpSpreadsheetを使ってExcelファイルに出力し、クライアントのダウンロードフォルダーにファイルをダウンロードさせたいです。

問題は、ファイルがクライアントのダウンロードフォルダーには落とせないとのことです。
phpspreadsheetのrecipesを読むと、$writer->save('php://output')でクライアントにダウンロードさせられると書いてましたが、以下のソースを実行すると、エラーや警告もなく動くけどファイルが生成されません。ブラウザーのデバッグツールでも問題なく、ajax値も成功が戻ってきました。
ファイル名が日本語になっているため、Header情報には文字化けしていたので、英語のファイル名に変えてみても同じでした。
(PS:もし、ファイル名を文字化けせずにHeaderに設定できる方法もあれば教えて頂きたいです。)

$writer->save($outputFilename); のように、ファイル名を設定すると、プロジェクト経路のbinフォルダーに正しくファイルが生成されます。(この場合もHeaderのファイル名は文字化けしますが、ダウンロードされたファイル名は正しく表示されます。)

何が問題なのか、全く分からず…三日間この問題にハマって全然進めない状況です。
何かヒントや解決策があれば教えてください。よろしくお願いします。

php

1<?php 2// Excel出力するためのプラグイン 3use PhpOffice\PhpSpreadsheet\Writer\Xls; 4use PhpOffice\PhpSpreadsheet\Spreadsheet; 5require_once(ROOT .DS. "vendor" . DS . "autoload.php"); 6 7public function sectionsheet(){ 8 …省略 9 if ($this->request->is('ajax')) 10 { 11  //ファイル名を日本語にすると、Headerで文字化けする。 12  $outputFilename = "2018年度帳票.xls"; 13  //テスト用のシートを作る 14  $spreadsheet = new Spreadsheet(); 15  $spreadsheet->setActiveSheetIndex(0) 16 ->setCellValue('A1', '日本語')17  $spreadsheet->getActiveSheet()->setTitle('Simple'); 18   $spreadsheet->setActiveSheetIndex(0); 19  //ダウンロードするためのヘッダー設定 20  header('Content-Type: application/vnd.ms-excel'); 21 header('Content-Disposition: attachment;filename="'.$outputFilename.'"'); 22 header('Cache-Control: max-age=0'); 23  //Excelファイルを出力する。 24  $writer = new Xls($spreadsheet); 25  //問題はこの部分、$writer->save($outputFilename); は正常動作します。(プロジェクトのbinフォルダーにファイル生成される。) 26 $writer->save('php://output'); 27 28 exit; 29 30 } 31}

追加:色々調べた結果、CakephpのCallbackStreamを利用する、との方法もあり、以下のように書いてみましたが、同じ結果でした。

PHP

1//public function sectionsheet()の中 2$stream = new \Cake\Http\CallbackStream(function(){ 3 $spreadsheet = new Spreadsheet(); 4 $spreadsheet->setActiveSheetIndex(0) 5 ->setCellValue('A1', '日本語'); 6 $spreadsheet->getActiveSheet()->setTitle('Simple'); 7 $spreadsheet->setActiveSheetIdex(0); 8 9 header('Content-Type: application/vnd.ms-excel'); 10 header('Content-Disposition: attachment;filename="2018年度帳票.xls"'); 11 header('Cache-Control: max-age=0'); 12 13 $writer = new Xls($spreadsheet); 14 $writer->save('php://output'); 15 exit; 16 17}); 18 19return $this->response->withFile($stream, ['download' => true, 'name' => "2018年度帳票.xls"]);

参考:AJAXはこんな風に書きました。ブラウザーのデバッグツールで確認すると、$writer->save('php://output');で動かすと、dataに文字化けした値がリターンされますが、多分出力されたファイルが化けて表示されていると思われます。

AJAX

1$.ajax({ 2  url:urlPath, 3 type:'POST', 4 data:formData, 5 cache:false 6}) 7.done(function(data,status) 8{ 9 if(status) 10 { 11  alert('成功?'); 12 } 13 location.reload(); 14}) 15.fail(function(data){ 16 alert('失敗'); 17 location.reload( 18});

追加:リスポンスされた時のHeader情報は以下のようになっています。(ファイル名は、日本語になっています)
イメージ説明

また追加:以下のように、出力部だけ別の関数にし、ブラウザーのURLに /sheets/sectionsheetouput と入力すると、クライアントに落ちることまで確認しました。

PHP

1public function sectionsheetoutput() 2 { 3 $this->autoRender = false; 4 $this->viewBuilder()->setLayout(false); 5 6 // Excel出力するためのファイル名とフォルダー名を設定する。 7 date_default_timezone_set('Asia/Tokyo'); 8 $outputDate = date('Ymd_His'); 9 $outputFilename = $outputDate . "_test.xlsx"; 10 11 $spreadsheet = new Spreadsheet(); 12 13 $spreadsheet->setActiveSheetIndex(0) 14 ->setCellValue('A1', '日本語_japanese'); 15 16 $spreadsheet->getActiveSheet()->setTitle('Simple'); 17 $spreadsheet->setActiveSheetIndex(0); 18 19 // header('Content-Type: application/vnd.ms-excel'); 20 header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'); 21 header('Content-Disposition: attachment;filename="'.$outputFilename.'"'); 22 header('Cache-Control: max-age=0'); 23 $writer = IOFactory::createWriter($spreadsheet, 'Xlsx'); 24 $writer->save('php://output'); 25 exit; 26 }

追加に追加:$writer->save('php://output'); にすると、やはりヘッダーについてエラーがでました。

ERROR

12018-12-18 17:43:32 Warning: Warning (512): Unable to emit headers. Headers sent in file=D:\Bitnami\wampstack-7.1.7-0\apache2\htdocs\djks\trunk\vendor\phpoffice\phpspreadsheet\src\PhpSpreadsheet\Writer\Xlsx.php line=404 in [D:\Bitnami\wampstack-7.1.7-0\apache2\htdocs\djks\trunk\vendor\cakephp\cakephp\src\Http\ResponseEmitter.php, line 48] 2Request URL: /sheets/sectionsheet 3Referer URL: http://localhost:8765/sheets/sectionsheet 4Trace: 5Cake\Error\BaseErrorHandler::handleError() - CORE\src\Error\BaseErrorHandler.php, line 153 6Cake\Http\ResponseEmitter::emit() - CORE\src\Http\ResponseEmitter.php, line 48 7Cake\Http\Server::emit() - CORE\src\Http\Server.php, line 106 8[main] - ROOT\webroot\index.php, line 40 9 10 11 122018-12-18 17:43:32 Warning: Warning (2): Cannot modify header information - headers already sent by (output started at D:\Bitnami\wampstack-7.1.7-0\apache2\htdocs\djks\trunk\vendor\phpoffice\phpspreadsheet\src\PhpSpreadsheet\Writer\Xlsx.php:404) in [D:\Bitnami\wampstack-7.1.7-0\apache2\htdocs\djks\trunk\vendor\cakephp\cakephp\src\Http\ResponseEmitter.php, line 148] 13Request URL: /sheets/sectionsheet 14Referer URL: http://localhost:8765/sheets/sectionsheet 15Trace: 16Cake\Error\BaseErrorHandler::handleError() - CORE\src\Error\BaseErrorHandler.php, line 153 17header - [internal], line ?? 18Cake\Http\ResponseEmitter::emitStatusLine() - CORE\src\Http\ResponseEmitter.php, line 148 19Cake\Http\ResponseEmitter::emit() - CORE\src\Http\ResponseEmitter.php, line 54 20Cake\Http\Server::emit() - CORE\src\Http\Server.php, line 106 21[main] - ROOT\webroot\index.php, line 40 22 23 24 252018-12-18 17:43:32 Warning: Warning (2): Cannot modify header information - headers already sent by (output started at D:\Bitnami\wampstack-7.1.7-0\apache2\htdocs\djks\trunk\vendor\phpoffice\phpspreadsheet\src\PhpSpreadsheet\Writer\Xlsx.php:404) in [D:\Bitnami\wampstack-7.1.7-0\apache2\htdocs\djks\trunk\vendor\cakephp\cakephp\src\Http\ResponseEmitter.php, line 181] 26Request URL: /sheets/sectionsheet 27Referer URL: http://localhost:8765/sheets/sectionsheet 28Trace: 29Cake\Error\BaseErrorHandler::handleError() - CORE\src\Error\BaseErrorHandler.php, line 153 30header - [internal], line ?? 31Cake\Http\ResponseEmitter::emitHeaders() - CORE\src\Http\ResponseEmitter.php, line 181 32Cake\Http\ResponseEmitter::emit() - CORE\src\Http\ResponseEmitter.php, line 55 33Cake\Http\Server::emit() - CORE\src\Http\Server.php, line 106 34[main] - ROOT\webroot\index.php, line 40 35 36 37 382018-12-18 17:43:32 Warning: Warning (2): Cannot modify header information - headers already sent by (output started at D:\Bitnami\wampstack-7.1.7-0\apache2\htdocs\djks\trunk\vendor\phpoffice\phpspreadsheet\src\PhpSpreadsheet\Writer\Xlsx.php:404) in [D:\Bitnami\wampstack-7.1.7-0\apache2\htdocs\djks\trunk\vendor\cakephp\cakephp\src\Http\ResponseEmitter.php, line 181] 39Request URL: /sheets/sectionsheet 40Referer URL: http://localhost:8765/sheets/sectionsheet 41Trace: 42Cake\Error\BaseErrorHandler::handleError() - CORE\src\Error\BaseErrorHandler.php, line 153 43header - [internal], line ?? 44Cake\Http\ResponseEmitter::emitHeaders() - CORE\src\Http\ResponseEmitter.php, line 181 45Cake\Http\ResponseEmitter::emit() - CORE\src\Http\ResponseEmitter.php, line 55 46Cake\Http\Server::emit() - CORE\src\Http\Server.php, line 106 47[main] - ROOT\webroot\index.php, line 40

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2018/12/18 04:14

Excel出力する場面のhttpヘッダー類がどうなっているか、webブラウザ側の開発ツールなどで観察できませんか?
lovelydai

2018/12/18 04:31

質問ありがとうございます。ヘッダー情報の図を追加しましたので参考にしてください。
m.ts10806

2018/12/18 06:25

そもそもajaxは裏で非同期実行するのでそこでattachを送信しても意味がないように思います。作るのはajaxにしろ実画面でattachを送信する必要があるのでは。作成されないのは作成するように作ってなくてダウンロードさせてるからだと思いますが、
lovelydai

2018/12/18 07:05

ありがとうございます。帳票出力すると用量が大きい場合、時間がかかるのでモーダルウィンドウなどを表示するためにAJAX使った方がいい、とのことでしたので使いました。PHPSPREADSHEETのサンプルみると、このくらいのコードで動いてましたが、これではだめですかね。
guest

回答1

0

php://output を指定すると、echoと同じように、中身を表示する形になります。
生レスポンスを生成して返す(自分でHeaderを書いて書き出す)形ならコレでいいのですが、どうもその後で
$this->response->withFile()と、FWの機能でレスポンスを返そうとしているのでおかしくなっているのだと思います。
サーバにファイルを残したい場合はファイル名を指定しそのファイルをwithFile()で返す、残す必要がなければphp://outputで書き出す、という形に統一すればいいのでは?

投稿2018/12/20 04:32

kunai

総合スコア5405

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

lovelydai

2018/12/20 09:06

回答ありがとうございます。そのコードを消して統一させてみましたが、やはりきませんでした。もっと探してみたら、Cakephp3ではストリームレスポンスを用いて標準バッファーに出力し、それを return $this->response->withBody($stream);で返す形でできると書いてましたが、まだ上手く動いてないですね。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問