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

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

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

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

Q&A

解決済

4回答

994閲覧

データのCSVダウンロード。列が増えるとダウンロードできない

mikeko0901

総合スコア227

PHP

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

0グッド

1クリップ

投稿2020/10/09 06:17

CSVのダウンロード処理を実装しておりますが、うまく行くときと行かない時があり、アドバイスをいただきたいです。。
色々と条件を変えてみて、どうやら列が増えるとダウンロードに失敗するようです。
列が増えるとダウンロードできない、などありますでしょうか??
以下、コードになります。

###コード

common.php (ダウンロード用関数)

function download_csv($file_name, $data) { $fp = fopen('php://output', 'w'); // UTF-8からSJIS-winへ変換するフィルター stream_filter_append($fp, 'convert.iconv.UTF-8/CP932//TRANSLIT', STREAM_FILTER_WRITE); foreach ($data as $row) { fputcsv($fp, $row, ',', '"'); } fclose($fp); header('Content-Type: application/octet-stream'); header("Content-Disposition: attachment; filename={$file_name}"); header('Content-Transfer-Encoding: binary'); exit(); }

test.php ★成功時 (入れるデータをemailまででストップ)

<?php require_once("../common/common.php"); //orderテーブル if(connect_db()) { $sql = "SELECT * FROM {$dbname}.order ORDER BY created_at DESC;"; $stmt = $dbh->prepare($sql); $stmt->execute(); $records = $stmt->fetchAll(); } else { } //ダウンロード用のデータを成型 $data = []; $i = 0; $data[$i] = [ //ヘッダーをセット "注文番号", "購入商品", "個数", "金額", "ChargeId", "ステータス", "納品日", "名前", "カナ", "Email", "電話番号", "郵便番号", "住所", "お得な情報", "メモ", "ご注文日時" ]; foreach($records as $record) { $i++; $data[$i][] = $record["order_id"]; $data[$i][] = $record["product_name"]; $data[$i][] = $record["buy_num"]; $data[$i][] = $record["product_price"] * $record["buy_num"]; $data[$i][] = $record["charge_id"]; $data[$i][] = get_status($record["status"],$record["auto_canceled"]); $data[$i][] = $record["sended_day"]; $data[$i][] = $record["name"]; $data[$i][] = $record["name_kana"]; $data[$i][] = $record["email"]; } $file_name = "test.csv"; download_csv($file_name, $data);

test.php ★失敗時 (入れるデータを全て)

<?php require_once("../common/common.php"); //orderテーブル if(connect_db()) { $sql = "SELECT * FROM {$dbname}.order ORDER BY created_at DESC;"; $stmt = $dbh->prepare($sql); $stmt->execute(); $records = $stmt->fetchAll(); } else { } //ダウンロード用のデータを成型 $data = []; $i = 0; $data[$i] = [ //ヘッダーをセット "注文番号", "購入商品", "個数", "金額", "ChargeId", "ステータス", "納品日", "名前", "カナ", "Email", "電話番号", "郵便番号", "住所", "お得な情報", "メモ", "ご注文日時" ]; foreach($records as $record) { $i++; $data[$i][] = $record["order_id"]; $data[$i][] = $record["product_name"]; $data[$i][] = $record["buy_num"]; $data[$i][] = $record["product_price"] * $record["buy_num"]; $data[$i][] = $record["charge_id"]; $data[$i][] = get_status($record["status"],$record["auto_canceled"]); $data[$i][] = $record["sended_day"]; $data[$i][] = $record["name"]; $data[$i][] = $record["name_kana"]; $data[$i][] = $record["email"]; $data[$i][] = $record["phone"]; $data[$i][] = $record["zip_code"]; $data[$i][] = $record["address"]; $data[$i][] = $record["getinfo"]; $memo = preg_replace('/\n|\r|\r\n/', ' ', $record["memo"] ); $data[$i][] = $memo; $data[$i][] = $record["created_at"]; } $file_name = "test.csv"; download_csv($file_name, $data);

ちなみに、データを作っているforeachの部分を、

foreach($records as $record) { $i++; $data[$i][] = $record["order_id"]; $data[$i][] = $record["product_name"]; $data[$i][] = $record["buy_num"]; $data[$i][] = $record["product_price"] * $record["buy_num"]; $data[$i][] = $record["charge_id"]; $data[$i][] = get_status($record["status"],$record["auto_canceled"]); $data[$i][] = $record["sended_day"]; $data[$i][] = $record["name"]; $data[$i][] = $record["name_kana"]; $data[$i][] = $record["email"]; $data[$i][] = $record["phone"]; }

にしても失敗。

emailまでだと成功しますが、emailの代わりにphoneにしても成功します。
↓こちらは成功。

foreach($records as $record) { $i++; $data[$i][] = $record["order_id"]; $data[$i][] = $record["product_name"]; $data[$i][] = $record["buy_num"]; $data[$i][] = $record["product_price"] * $record["buy_num"]; $data[$i][] = $record["charge_id"]; $data[$i][] = get_status($record["status"],$record["auto_canceled"]); $data[$i][] = $record["sended_day"]; $data[$i][] = $record["name"]; $data[$i][] = $record["name_kana"]; $data[$i][] = $record["phone"]; }

エラーの時は、特にエラー文が出るというわけでもなく、
中身が画面に表示されます。
イメージ説明
↑このような感じです。
成功するときは、このような文字化けはなく、正常にCSVダウンロードされます。
イメージ説明

アドバイスをいただけますと幸いです。何卒、よろしくお願いいたします。

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

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

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

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

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

guest

回答4

0

ベストアンサー

header設定を関数の頭に移動すればどうでしょうか?

投稿2020/10/09 08:55

attercop

総合スコア246

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

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

mikeko0901

2020/10/10 06:23 編集

ありがとうございます。 headerを前に持ってきて、 ``` function download_csv($file_name, $data) { $fp = fopen('php://output', 'w'); header('Content-Type: application/octet-stream'); header("Content-Disposition: attachment; filename={$file_name}"); header('Content-Transfer-Encoding: binary'); // UTF-8からSJIS-winへ変換するフィルター stream_filter_append($fp, 'convert.iconv.UTF-8/CP932//TRANSLIT', STREAM_FILTER_WRITE); foreach ($data as $row) { fputcsv($fp, $row, ',', '"'); } fclose($fp); exit(); } ``` しましたらできました! なぜ下の時は失敗していたのかはわかりませんが・・・
guest

0

追記

質問を読み誤っていたようなので追記します。

  • 発生している問題

発生している問題は、おそらくHTTPレスポンスヘッダが正常に出力されていないか、HTTPレスポンスヘッダ出力の前に何らかの出力(例えばエラーメッセージなど)があるため

PHP

1 header('Content-Type: application/octet-stream'); 2 header("Content-Disposition: attachment; filename={$file_name}"); 3 header('Content-Transfer-Encoding: binary');

で出力しているHTTPレスポンスヘッダが正常に動作していない
と思われます。

  • 出力の確認
  1. 開発者ツール(F12)でHTTPレスポンスヘッダを確認し、成功時と失敗時の差を比較する
  2. 失敗した時に右クリック→ソースを表示でHTMLソースを表示して先頭の方にエラー等が表示されていないか確認する(文字化けしている時は文字コードを変えて確認してみる)
  3. attercopさんの回答にあるheader()を先頭に持ってくるを試してみる(その場合、エラーメッセージ交じりのCSVがダウンロードできるはず)
  • 解決方法

エラーメッセージが出ているのであれば、その原因を修正する

以下、追記前の回答

データの流れを一つずつデバッグしていくしか無いですが、

自分がデバッグするなら、まずは、
適当なテキストエディタ(サクラエディタとかPHPを編集しているエディタ等の改行コードや文字コードをきちんと扱えるもの)で開いてみて、CSV自体が壊れていないか、表示文字コードを変更してみて問題無く開ける条件が無いか等を確認してみます。

次にこの辺を

PHP

1 $data[$i][] = $record["zip_code"]; 2 $data[$i][] = $record["address"]; 3 $data[$i][] = $record["getinfo"]; 4 $memo = preg_replace('/\n|\r|\r\n/', ' ', $record["memo"] ); 5 $data[$i][] = $memo; 6 $data[$i][] = $record["created_at"];

PHP

1 $data[$i][] = ""; 2 $data[$i][] = ""; 3 $data[$i][] = ""; 4 $data[$i][] = ""; 5 $data[$i][] = "";

として、問題が列そのものなのか、中に入っているデータなのかを切り分けると思います。
その後、列の値を一つずつ元の変数に戻していって問題発生の列を特定

問題の発生条件が明確になったら、

  • 入力時の問題(変な文字列が入っていないか、データを変えて試してみる)
  • 出力時の問題(stream_filter_append()を使わずにUTF-8のまま送信したり、mb_convert_encoding()で文字列変換して見たりを試す)

あたりを確認して修正方針を決めます。

投稿2020/10/09 07:58

編集2020/10/09 09:08
tanat

総合スコア18727

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

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

mikeko0901

2020/10/09 08:40

ありがとうございます。 ダウンロードが成功したCSVファイルはテキストエディアで開きましたが、特に壊れているとかはなさそうでした。 ``` $data[$i][] = $record["order_id"]; $data[$i][] = $record["product_name"]; $data[$i][] = $record["buy_num"]; $data[$i][] = $record["product_price"] * $record["buy_num"]; $data[$i][] = $record["charge_id"]; $data[$i][] = get_status($record["status"],$record["auto_canceled"]); $data[$i][] = $record["sended_day"]; $data[$i][] = $record["name"]; $data[$i][] = $record["name_kana"]; $data[$i][] = $record["email"]; $data[$i][] = ""; $data[$i][] = ""; ``` では成功してダウンロードできましたが、 ``` $data[$i][] = $record["order_id"]; $data[$i][] = $record["product_name"]; $data[$i][] = $record["buy_num"]; $data[$i][] = $record["product_price"] * $record["buy_num"]; $data[$i][] = $record["charge_id"]; $data[$i][] = get_status($record["status"],$record["auto_canceled"]); $data[$i][] = $record["sended_day"]; $data[$i][] = $record["name"]; $data[$i][] = $record["name_kana"]; $data[$i][] = $record["email"]; $data[$i][] = ""; $data[$i][] = ""; $data[$i][] = ""; ``` では失敗しました・・・ ですので、入っている文字が関係しているというわけではないのかな・・・と思いました。。
tanat

2020/10/09 08:48

> ダウンロードが成功したCSVファイルはテキストエディアで開きましたが、特に壊れているとかはなさそうでした。 質問を誤解していたかもしれません。 「ダウンロード失敗」は質問中のスクリーンショットがブラウザで表示されてしまうという事ですか?
tanat

2020/10/09 09:09 編集

追記しました。
mikeko0901

2020/10/10 06:21

ご教示いただきまして、ありがとうございます。 header関数を前に持ってきましたらできました!
guest

0

http response headerは他の出力が行われる前に出力されなければなりません。
その為、fpucsvの前にheader関数を実行する必要があります。

なお、convert.iconvフィルタは行の中で変換できない文字が存在した場合、行そのものの出力が無かったことになります。
住所・氏名など多様な文字が入力されるケースが想定される場合、使うべきではありません。

上記問題の更なる解説と回避方法はこちらの記事をご参照ください。
https://qiita.com/wakabadou/items/84b48ca12f25fb2fb69c

投稿2020/10/13 07:12

wakabadou

総合スコア31

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

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

mikeko0901

2020/10/16 05:48

ありがとうございます。お礼が遅くなり申し訳ございません。 convert.iconvフィルタについても勉強になりました。ありがとうございました。
guest

0

列が増える

ちょっとあいまいすぎです。
もちろん列がべらぼうに増えればメモリの問題がでてくるかもしれませんが
普通にfputcsvで処理できると思いますが・・・

投稿2020/10/09 06:55

yambejp

総合スコア116724

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問