yano3nora
7/1/2017 - 8:39 AM

[php: SplFileInfo/SplFileObject] import / export csv using SplFileObject. #php #cakephp

[php: SplFileInfo/SplFileObject] import / export csv using SplFileObject. #php #cakephp

<?php 

/**
 * # TIPS
 * ## touch() -> permission denied
 * http://www.iwaking.com/20121210/740/
 * ------------------------------------ */

App::uses('Component', 'Controller');
/**
 * upload & download file handler class
 * @category App\Controller\Component
 * @var Controller $controller
 * @var Request $request
 * @var string $encoding
 * @var string $decoding
 */
class CsvComponent extends Component {

  public $controller;
  public $request;
  private $encoding = ENCODING;
  private $decoding = DECODING;

  // before 'beforeFilter'
  public function initialize(Controller $controller) {
  }

  // after 'beforeFilter'
  public function startup(Controller $controller) {
    $this->controller = $controller;
    $this->request = $controller->request;
  }

  // before 'render'
  public function beforeRender(Controller $controller) {
  }

  // before exit
  public function shutdown(Controller $controller) {
  }

  // before redirect (redirect would canceled when return false from this)
  public function beforeRedirect(Controller $controller, $url, $status=null, $exit=true) {
  }

  /**
   * import csv file with encoding to $this->encoding
   * @param str $prefix
   * @param array $file (e.g.) $this->request->data('User.upload_file')
   * @param bool $useHeader (remove first row perhaps header)
   * @return array $csv (on success)
   * @return bool false (on failure)
   * @throws Exception
   */
  public function csvImportWithEncoding($prefix, $file, $useHeader=false) {
    $csv = array(); //init
    if (is_uploaded_file($file['tmp_name']) && pathinfo($file['name'], PATHINFO_EXTENSION) == 'csv') {
      $fileName = UPLOAD_DIR.$prefix.'_'.$this->controller->name.'_'.session_id().'.csv';
      move_uploaded_file($file['tmp_name'], $fileName);
      $rawFile = file_get_contents($fileName);
      $encodedFile = mb_convert_encoding($rawFile, $this->encoding, $this->decoding);
      file_put_contents($fileName, $encodedFile);
      if (!file_exists($fileName)) throw new Exception('ファイルエンコードに失敗しました。');
      $fileObj = new SplFileObject($fileName);
      $fileObj->setFlags(SplFileObject::READ_CSV);
      $fileObj->setCsvControl(",", "\n");  //delimiter
      $isAtFirst = true;
      foreach ($fileObj as $line) {
        if ($isAtFirst && !$useHeader) $isAtFirst = false; continue;
        if (is_null($line[0])) continue;
        $csv[] = $line;
      }
      if (!$csv) throw new Exception('ファイル読み取りに失敗しました。データを確認してください。');
      return $csv;
    } else {
      throw new Exception('ファイルアップロードに失敗しました。形式がCSVであるかを確認してください。');
      return false;
    }
  }

  /**
   * export csv file with encoding to $this->encoding
   * @param str $prefix
   * @param array $data
   * @param array $header
   * @return file readfile($filePath) 
   * @throws Exception
   */
  public function csvExportWithEncoding($filename, $data, $header) {
    $filePath = DOWNLOAD_DIR.$filename.'.csv';
    // encoding
    mb_convert_variables($this->decoding, $this->encoding, $header);
    mb_convert_variables($this->decoding, $this->encoding, $data);
    // create csv file
    if (!touch($filePath)) throw new Exception('CSV一時ファイル生成に失敗しました。');
    $fileObj = new SplFileObject($filePath, 'w');
    if (array_depth($header) >= 2) {
      foreach ((array)$header as $headerLine) {
        $fileObj->fputcsv($headerLine);
      }
    } else {
      $fileObj->fputcsv($header);
    }
    foreach ((array)$data as $line) {
      $fileObj->fputcsv($line);
    }
    // print csv file
    header("Pragma: public"); 
    header("Expires: 0");
    header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
    header("Cache-Control: private", false); // required for certain browsers 
    header("Content-Transfer-Encoding: binary");
    header("Content-Disposition: attachment; filename=".basename($filePath));
    header("Content-Type: application/force-download");
    return readfile($filePath);
  }

}

what's ?

PHPの標準組込クラスとして SplFileObject というのがあり、ファイル(主にテキストファイル)をどうこうするのに向いているそうな。 SplFileInfo を継承しているのでファイル情報もこいつからとれる。spl (sqlぢゃないよ) は Standard PHP Library の略。

refs


how to use

notice

律儀なやつなのでちゃんと面倒見ること

最終行の改行や空行もちゃあんと認識して登録する真面目クンなので、そのあたりはこっちで教えてあげる。

SJIS-winなんて消えてなくなればいいね

fgetでもこいつでもやっぱりSJIS形式のCSVは扱いに困る。ループ中で mb_convert_encoding() するのも良いが、稀にうまくいかない。読み取り前にアップロードされたファイルを一度全てUTF8にコンバートしてからごにょるのが楽。