21CTO 导读: 在现在这篇文章中, 我们将一起学习处理 PHP 中的大文件, 避免因内存限制而无法使用的方法.
如果你想用 PHP 处理大文件, PHP 提供了一些普通的 PHP 函数, 比如 file_get_contents() 或 file() 函数, 它们在处理超大文件时会存在一定局限性.
其中包括:
1. 内存限制
上面这些函数依赖于 php.ini 中 memory_limit 的参数值设置, 可以调整增加这些值, 但这些函数仍然不适合极大的文件, 因为这些函数会把整个文件内容都读入到内存中.
如果文件的尺寸超过 memory_limit 的设置, 这类文件都不会加载到内存中. 现实中, 比如我们有一个 20G 的的文件, 想用 PHP 来处理, 该怎么样处理?
2. 不太好的用户体验
还有一个限制是生产环境的速度问题. 如果我们把它输出到数组中, 这样经常会出现在浏览器出现很长时间的等待, 白页或者出错等情况. 这样给用户的体验不太好.
对于这种技术限制, 可以使用 yield 关键字来直接生成一个结果.
SplFileObject Class
在本文中, 我们告诉大家使用 PHP 标准库中的 SplFileObject 类.
我们来演示, 创建一个类来处理大文件.
这个类使用文件名做为输入参数. 如下代码:
- class BigFile
- {
- protected $file;
- public function __construct($filename, $mode = 'r')
- {
- if (!file_exists($filename)) {
- throw new Exception('File not found');
- }
- $this->file = new SplFileObject($filename, $mode);
- }
- }
接下来, 我们定义一个遍历文件的方法, 这个方法将使用 fgets() 函数一次读取文件一行.
我们也可以使用 fread() 函数的方法.
读取文本文件
fgets() 适用于解析包含换行符的文本文件, 而 fread() 适用于解析二进制文件.
该函数用于遍历文本文件的每一行内容.
- protected function iterateText()
- {
- $count = 0;
- while (!$this->file->eof()) {
- yield $this->file->fgets();
- $count++;
- }
- return $count;
- }
读取二进制文件
来看另一个读取二进制的文件:
- protected function iterateBinary($bytes)
- {
- $count = 0;
- while (!$this->file->eof()) {
- yield $this->file->fread($bytes);
- $count++;
- }
- }
直接读取
现在我们将定义一个采用迭代的类型, 返回 NoRewindIterator 实例的方法.
我们使用 NoRewindIterator 强制单向读取.
- public function iterate($type = 'Text', $bytes = NULL)
- {
- if ($type == 'Text') {
- return new NoRewindIterator($this->iterateText());
- } else {
- return new NoRewindIterator($this->iterateBinary($bytes));
- }
- }
整个类源文件如下如示:
- class BigFile
- {
- protected $file;
- public function __construct($filename, $mode = 'r')
- {
- if (!file_exists($filename)) {
- throw new Exception('File not found');
- }
- $this->file = new SplFileObject($filename, $mode);
- }
- protected function iterateText()
- {
- $count = 0;
- while (!$this->file->eof()) {
- yield $this->file->fgets();
- $count++;
- }
- return $count;
- }
- protected function iterateBinary($bytes)
- {
- $count = 0;
- while (!$this->file->eof()) {
- yield $this->file->fread($bytes);
- $count++;
- }
- }
- public function iterate($type = 'Text', $bytes = NULL)
- {
- if ($type == 'Text') {
- return new NoRewindIterator($this->iterateText());
- } else {
- return new NoRewindIterator($this->iterateBinary($bytes));
- }
- }
- }
解析大文件:
我们对类文件做如下测试:
- $largefile = new BigFile('file.csv');
- $iterator = $largefile->iterate('Text'); // Text or Binary based on your file type
- foreach ($iterator as $line) {
- echo $line;
- }
现在这个类可以读取任何尺寸的大文件, 没有任何限制, 无限大.
我们可以在 Laravel 项目中, 将该类添加到 composer.json 文件中自动加载该类并直接使用.
从此, 我们可以轻松使用 PHP 解析和处理大尺寸文件了.
Happy PHP Coding!
来源: http://www.92to.com/bangong/2018/05-20/33814024.html