PHP でデータをストリームする方法
Takahiro Iwasa
2 min read
PHP
It has been more than 3 years since this post was published.
PHP は php://output
を使用してレスポンスをストリーミングできます。
これは大量のデータを処理する際に便利です。
php://output is a write-only stream that allows you to write to the output buffer mechanism in the same way as print and echo.
PHP 7.1 の例
URL Copied!
以下は、ユーザーが CSV ファイルをダウンロードできるようにする簡単な例です。
重要なポイントは、 fopen('php://output', 'w');
でストリームを開く部分です。
App\Lib\Stream\DownloadStream.php
URL Copied!
<?php
namespace App\Lib\Stream;
/**
* Class DownloadStream
* @package App\Lib\Stream
*/
class DownloadStream
{
public static function getStream(string $fileName)
{
// Send header
header("Content-Type: application/octet-stream");
header("Content-Disposition: attachment; filename={$fileName}");
// Get output stream
$stream = fopen('php://output', 'w');
// Filter to conver character code to ShiftJIS
stream_filter_prepend($stream, 'convert.iconv.utf-8/cp932');
// Filter to convert newline to CRLF
stream_filter_register('CrlfFilter', 'App\Lib\Stream\Filter\CrlfFilter');
stream_filter_append($stream, 'CrlfFilter');
return $stream;
}
}
App\Lib\Stream\Filter\CrlfFilter.php
URL Copied!
これは行末を CRLF に変換するストリームフィルターです。 データのストリーミングには直接関係ありませんが、 Windows ユーザーにとって便利です。
<?php
namespace App\Lib\Stream\Filter;
/**
* Class CrlfFilter
* @package App\Lib\Stream\Filter
*/
class CrlfFilter extends \php_user_filter
{
/**
* Callback method when this filter is applied
* @param resource $in
* @param resource $out
* @param int $consumed
* @param bool $closing
* @return int
*/
public function filter($in, $out, &$consumed, $closing)
{
while ($bucket = stream_bucket_make_writeable($in)) {
// Clear newline codes in advance
$bucket->data = preg_replace("/\r$/", "", $bucket->data);
$bucket->data = preg_replace("/\n$/", "", $bucket->data);
// Append CRLF
$bucket->data = $bucket->data . "\r\n";
$consumed += $bucket->datalen;
stream_bucket_append($out, $bucket);
}
return PSFS_PASS_ON;
}
}
使用法
URL Copied!
以下は使用例です。
// Filename
$fileName = 'test.csv';
// Get download stream
$stream = DownloadStream::getStream($fileName);
// Output header
fputcsv($stream, [
'last_name',
'first_name',
'company',
]);
// Output record
fputcsv($stream, [
'Jane',
'Doe',
'ABC Company',
]);
// Close stream
fclose($stream);
まとめ
URL Copied!
ユーザーに100万レコードを含む CSV ファイルをダウンロードさせるようなケースの場合、 php://output
を利用できます。
この投稿が、お役に立てば幸いです。