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

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

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

CSV(Comma-Separated Values)はコンマで区切られた明白なテキスト値のリストです。もしくは、そのフォーマットでひとつ以上のリストを含むファイルを指します。

PHP

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

Laravel 5

Laravel 5は、PHPフレームワークLaravelの最新バージョンで、2014年11月に発表予定です。ディレクトリ構造がが現行版より大幅に変更されるほか、メソッドインジェクションやFormRequestの利用が可能になります。

Q&A

解決済

2回答

5735閲覧

【解決】PHP Laravel5.1でのCSV出力の際、1行目が空白行になってしまう問題

hika-rakuyo

総合スコア15

CSV

CSV(Comma-Separated Values)はコンマで区切られた明白なテキスト値のリストです。もしくは、そのフォーマットでひとつ以上のリストを含むファイルを指します。

PHP

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

Laravel 5

Laravel 5は、PHPフレームワークLaravelの最新バージョンで、2014年11月に発表予定です。ディレクトリ構造がが現行版より大幅に変更されるほか、メソッドインジェクションやFormRequestの利用が可能になります。

0グッド

0クリップ

投稿2021/04/03 02:43

編集2021/04/03 06:47

前提

開発環境 AWS cloud9 (t2.micro)
使用言語 PHP7.0
フレームワーク Laravel 5.1.6
データベース MySQL 5.7.33

前提といたしまして、当方は既存のプロジェクトの改良に携わっているという背景があります。
下記コーディングの殆どは既に記述のあったもので、勉強のために自分でコメントを振り少しずつ解釈しているところです。
最悪下記の手法に捉われることなく一から書き直すことも念頭に置いてはいるのですが、極力既存のコードを修正する方向で問題を解消したいと考えています。
上記ご理解いただける方にご助力いただけますと幸甚です。

実現したい内容

「データベースに登録された情報を用いてCSVファイルを作成・ダウンロードする」

テーブル内容例

my_project.users +----+------+-------------+ | id | name | phone | +----+------+-------------+ | 1 | Nick | 090******** | +----+------+-------------+ | 2 | Mary | 080******** | +----+------+-------------+ | 3 | Kate | 070******** | +----+------+-------------+

発生している問題

CSV出力及びダウンロードは申し分なく行えるのですが、
タイトルにも記載の通り、エクスポートされたCSVの1行目が空白行になってしまいます。
できるならばこれを避けて、1行目からデータ行を書き込みたいと考えています。

sakura editorで確認したところ、先頭にLF改行が含まれているようです。
CSVファイルのスクリーンショット

CSV

1(LF改行) 21,Nick,090******** 32,Mary,080******** 43,Kate,070********

コーディング

  • Class (/MyProject/app/Repositories/CSV.php)

php

1<?php 2 3namespace App\Repositories; 4 5use Response; 6use Config; 7use Illuminate\Support\Facades\Log; 8 9class CSV { 10 public function __construct() { 11 } 12 13 public function download($list, $header, $filename) { 14 // ob_start(); *** ① 15 // ヘッダーが指定されていたら、配列の最初に加える 16 if (count($header) > 0) { 17 array_unshift($list, $header); 18 19 } 20 21 // 読込・書出用の一時ファイルをバイナリーモードで開く 22 $stream = fopen('php://temp', 'r+b'); 23 // CSVソースの準備。配列に含まれる配列をひとつづつ挿入する 24 foreach ($list as $row) { 25 fputcsv($stream, $row); 26 } 27 // ファイルポインタをファイル先頭に戻す 28 rewind($stream); 29 // ファイルの文字列を取出し 30 $csv = stream_get_contents($stream); 31 /** Ⅰ **/ 32 // 改行コードをCRLFに置き換える 33 $csv = str_replace(PHP_EOL, "\r\n", $csv); 34 /** Ⅱ **/ 35 // 文字コードをUTF-8からSJISに変換する 36 $csv = mb_convert_encoding($csv, 'SJIS-win', 'UTF-8'); 37 /** Ⅲ **/ 38 // レスポンスのヘッダー情報を設定 39 $headers = array( 40 'Content-Type' => 'text/csv', 41 'Content-Disposition' => "attachment; filename=$filename", 42 ); 43 // $out = ob_get_contents(); ***② 44 // ob_end_clean(); ***③ 45 // trim($out); ***④ 46 // レスポンスを返す 47 return \Response::make($csv, 200, $headers); 48 } 49}
  • Provider (/MyProject/app/Providers/CSVServiceProvider.php)

php

1<?php 2 3namespace App\Providers; 4 5use Illuminate\Support\ServiceProvider; 6use App\Repositories\CSV; 7 8class CSVServiceProvider extends ServiceProvider 9{ 10 /** 11 * Bootstrap the application services. 12 * 13 * @return void 14 */ 15 public function boot() 16 { 17 // 18 } 19 20 /** 21 * Register the application services. 22 * 23 * @return void 24 */ 25 public function register() 26 { 27 // クラスとファサードを結合し、サービスコンテナに登録? 28 $this->app->bind( 29 "csv", 30 "App\Repositories\CSV" 31 ); 32 } 33}
  • Facade (/MyProject/app/Facades/CSV.php)

php

1<?php 2 3namespace App\Facades; 4use Illuminate\Support\Facades\Facade; 5 6class CSV extends Facade { 7 8 public static function getFacadeAccessor() { 9 return 'csv'; 10 } 11}
  • Config (/MyProject/config/app.php)

php

1<?php 2 3return [ 4 // 中略 5 'providers' => [ 6 // 略 7 App\Providers\CSVServiceProvider::class 8 // 略 9 ], 10 'aliases' => [ 11 // 略 12 'CSV' => App\Facades\CSV::class, 13 // 略 14 ], 15];
  • Model (/app/User.php)

php

1<?php 2namespace App; 3use Illuminate\Database\Eloquent\Model; 4// 中略 5class User extends Model { 6 // 使用するテーブルの指定 7 protected $table='users'; 8}
  • Route (/MyProject/app/Http/routes.php)

php

1<?php 2 // 中略 3 Route::get('/users/index', 'UsersController@index')->name('users.csv'); 4 Route::post('/users/csv', 'UsersController@csv')->name('users.csv');
  • Controller (/MyProject/app/Http/Controllers/UsersController.php)

php

1<?php 2namespace App\Http\Controllers; 3 4use Illuminate\Http\Request; 5 6use App\Http\Requests; 7use App\Http\Controllers\Controller; 8 9use CSV; // 追加 10use User; // 追加 11 12class UsersController extends Controller 13{ 14 public function index() 15 { 16 // users全データの取り出し 17 $users = User::all(); 18 // viewを呼び出す 19 return view('users.index', ['users' => $users]); 20 } 21 // 中略 22 23 public function csv() 24 { 25 // usersの全データ取出し 26 $users = User::all(); 27 // 配列の準備 28 $arrayData = []; 29 // Collectionを回してobjectをひとつずつ処理する 30 foreach($users as $user) { 31 // 必要なデータを配列に格納し、その配列を$arrayDataに追加 32 $arrayData[] = [ 33 $user->id, 34 $user->name, 35 $user->phone, 36 ]; 37 } 38 // CSV出力(今回はヘッダーを指定しない) 39 return CSV::download($arrayData, null, 'users.csv'); 40 } 41
  • Blade (/MyProject/resources/views/users/index.blade.php)

php

1<!DOCTYPE html> 2<html lang="ja"> 3 <head> 4 <meta charset="utf-8"> 5 <title>MyProject - Users</title> 6 </head> 7 <body> 8 <h1>Users</h1> 9 <table> 10 <tr> 11 <th>id</th> 12 <th>name</th> 13 <th>phone number</th> 14 </tr> 15 @foreach($users as $user) 16 <tr> 17 <td>{{$user->id}}</td> 18 <td>{{$user->name}}</td> 19 <td>{{$user->phone}}</td> 20 </tr> 21 @endforeach 22 </table> 23 24 {!! Form::open(['route' => 'users.csv']) !!} 25 <button type="submit">CSV</button> 26 {!! Form::close() !!} 27 </body> 28</html>

(Controller及びBladeに関しては、こちらでの表示サンプルにつき動作未検証です。ご容赦ください。)

解決策候補

[0] PHP文書の開始タグ及び終了タグ前後のスペースや改行が原因か。
(参考: PHPで出力したCSVファイルの先頭に空行が入ってしまう)
これが関わっているとすると、CSV::download メソッドの返り値である
\Response::make($csv, 200, $headers);
の処理内部の問題かなと思うのです。
Laravelの根幹部に関わる、Responseクラス(ファサード?)について記述されたファイルが特定できれば精査するのですが…

[1] Classを記述した /MyProject/app/Repositories/CSV.php に追記。

  1. ヘッダーディレクティブの削除

(参考: php - fputcsvは.csvの先頭に空の行を挿入します)
具体的には、上記コードの①~④を追記してみました。
そもそもバッファというものについて全く無知なので、検索で見つけたものを書くだけ書いた、という感じです。
元のサイトとはコーディングの動きも違いますし、解消には至りませんでした。

  1. LF改行を無理やり取り除く

具体的には上記コードのⅠ~Ⅲのいずれかに
preg_replace("/\n/", "", $csv, 1);
を追記しました。最初に現れるLF改行のみを空文字に置き換える、という試みです。
いずれも、レコード間のCRLF改行に影響を与えるのみで、1行目の空白行は残ったままでした。

補足情報

前任者に話を聞くと、コーディング当初このような問題は発生しなかったようです。
いつからか空白行が入るようになったのは観測したと言っていました。具体的にいつ、というのは現状思い出せないそう。

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

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

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

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

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

guest

回答2

0

ベストアンサー

[0] PHP文書の開始タグ及び終了タグ前後のスペースや改行が原因か。

おそらくそれが原因ではないかと思います。

これが関わっているとすると、CSV::download メソッドの返り値である
\Response::make($csv, 200, $headers);
の処理内部の問題かなと思うのです。

いいえ、そうとは限りません。
例えば、routes.php ファイルの最初の行 <?php の前に試しに空白行を1行入れてみてください。CSVにその空白行が出力されるはずです。routes.php ファイルかどうかまではわかりませんが、いずれかのPHPファイルに余分な空白行が入っていないでしょうか?

投稿2021/04/03 05:05

Lulucom

総合スコア1899

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

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

hika-rakuyo

2021/04/03 06:27

初めまして、ご回答をいただきありがとうございます。 なるほど、PHPタグの問題は幅広くファイルを見直す必要がありそうですね。(実際にroutes.phpの先頭に改行を入れて確かめたところ、おっしゃったとおりの結果になりました。) 「タグ前後の改行を怪しめ」という記事はいくつかあったのですが「どのファイルが」と明確に書かれていなかったので路頭に迷っていました。的確なアドバイスをいただけて本当に助かります。 ちなみに、本文で掲げていないような別のControllerのファイルで改行を入れても変化はありませんでした。 ということは、この一連の動作に絡んでいるPHPファイルのいずれかが余分な改行を含んでいる、という認識でファイルを特定していけばいいのでしょうか。 一旦はプロジェクト内部にあるすべてのPHPファイルをあたってみますが…。
Lulucom

2021/04/03 06:29

> この一連の動作に絡んでいるPHPファイルのいずれかが余分な改行を含んでいる、という認識でファイルを特定していけばいいのでしょうか。 はい、そうだと思います。
hika-rakuyo

2021/04/03 06:38

ご返答ありがとうございます。 ご指摘いただいた通りに探ってみますね。
hika-rakuyo

2021/04/06 04:15 編集

`MyProject/public/index.php` が犯人でした。 本当にありがとうございました!
Lulucom

2021/04/03 06:46

いえいえ、よかったです^ ^
guest

0

正直良くわからないのですが、
php://temp
を使わずに例えば
PHP: tmpfile - Manual
を使ってファイルへの入出力としてやるならどうでしょう?

投稿2021/04/03 04:30

退会済みユーザー

退会済みユーザー

総合スコア0

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

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

hika-rakuyo

2021/04/03 06:32

初めまして、ご回答いただきありがとうございます。 自分自身もその部分の仕組みはほとんど理解できていなくて、むやみにいじれない現状があります。申し訳ありません。 後学として、時間のある時にドキュメントから勉強してみますね。 ありがとうございました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問