[英]Laravel CSV Import running out of memory (allowed memory exhausted)
我已经得到了我一个月包含〜6000行领取一次成员的CSV文件。
我正在(试图)遍历CSV文件,检查record
已在members
表中存在,如果存在,请检查record
是否相同。
然后将其插入pending
表(在适当的地方带有一个存在标志)。
我正在使用Laravel和League \\ CSV读取保存在我的storage
文件夹中的文件:
class ImportController extends Controller
{
public function import(Request $request) {
$readDirectory = 'storage/csv/';
$filename = $request->name;
$stream = fopen($readDirectory.$filename, 'r');
$reader = Reader::createFromStream($stream, 'r')->setHeaderOffset(0);
$records = (new Statement())->process($reader);
// Truncate the imported table prior to import
Imported::truncate();
foreach ($records as $record) {
$email = $record['email'];
$recordExists = $this->recordExists($email);
if($recordExists) {
// Compare the md5 of the recordArray and the memberArray and skip the record if thit's the same.
$memberArray = $this->getmemberArray($recordExists);
$recordArray = $this->getRecordArray($record);
if($memberArray['hash'] === $recordArray['hash']) { continue; }
$record['exists'] = TRUE;
$this->write($record);
continue;
}
else
{
$record['exists'] = FALSE;
$this->write($record);
Log::debug("missing: ".$record['URN']);
continue;
}
};
// End Foreach Loop
return redirect()->route('upload.show');
}
public function recordExists($urn){
$member = Member::where('email', 'LIKE', $email)->first();
if ($member == null) { return false; }
return $member;
}
public function getmemberArray($member) {
$memberArray = [
'email' => $member->email,
'first_name' => $member->first_name,
'last_name' => $member->last_name,
'age_years' => $member->age_years,
'gender' => $member->gender,
'address_1' => $member->address_1,
'address_2' => $member->address_2,
'address_3' => $member->address_3,
'town' => $member->town,
'county' => $member->county,
'postcode' => $member->postcode,
'sport_1' => $member->sport_1,
'sport_2' => $member->sport_2,
];
$memberArray['hash'] = md5(json_encode($memberArray));
return $memberArray;
}
public function getRecordArray($record) {
$recordArray = [
'email' => $record['email'],
'first_name' => $record['first_name'],
'last_name' => $record['last_name'],
'age_years' => $record['age_years'],
'gender' => $record['gender'],
'address_1' => $record['address_1'],
'address_2' => $record['address_2'],
'address_3' => $record['address_3'],
'town' => $record['town'],
'county' => $record['county'],
'postcode' => $record['postcode'],
'sport_1' => $record['sport_1'],
'sport_2' => $record['sport_2'],
];
$recordArray['hash'] = md5(json_encode($recordArray));
return $recordArray;
}
public function write($record) {
$import = [];
$import['email'] = $record['email'],
$import['first_name'] = $record['first_name'],
$import['last_name'] = $record['last_name'],
$import['age_years'] = $record['age_years'],
$import['gender'] = $record['gender'],
$import['address_1'] = $record['address_1'],
$import['address_2'] = $record['address_2'],
$import['address_3'] = $record['address_3'],
$import['town'] = $record['town'],
$import['county'] = $record['county'],
$import['postcode'] = $record['postcode'],
$import['sport_1'] = $record['sport_1'],
$import['sport_2'] = $record['sport_2'],
$import['exists'] = $record['exists']
DB::table('imported')->insert(
$import
);
Log::debug($record['email']);
return TRUE;
}
}
但我不断得到:
Symfony \\ Component \\ Debug \\ Exception \\ FatalErrorException (E_UNKNOWN) Allowed memory size of 134217728 bytes exhausted (tried to allocate 181321056 bytes)
如果我在CSV中使用的行少得多,则可以使用,但这不是一种选择。
我以前使用eloquent->save()
来写数据库,但是将其更改为DB::table()->insert
以提高性能。
为了测试目的,我已经添加了以下内容,但仍无法解决。
set_time_limit(0);
ini_set('max_execution_time', 100000);
ini_set('memory_limit','512m');
我想念什么吗? 某处内存泄漏?
我猜想它每次都会将记录保存在内存中,所以有什么方法可以让每一行都忘记它吗?
还:有一种方法可以清除此内存,以便我可以编辑代码并重试?
即使我停止并重新运行php artisan serve
它仍然保留相同的错误消息。
这里的问题是,当您执行以下操作时, League\\CSV
正在将整个CSV文件读取到内存中:
$records = (new Statement())->process($reader);
您应该像这样使用 Reader
的chunk
方法来一次只读取特定数量的行:
foreach($reader->chunk(50) as $row) { // do whatever }
chunk
方法返回一个可以迭代的生成器 。您可以在文档中找到此处提到的内容 。
编辑:我误读了文档,并建议使用错误的方法。
基本上,您只需要遍历$reader
本身:
foreach ($reader as $row) { print_r($row); }
另外,如果您使用的是Mac,或者您的CSV是在Mac上创建的,则需要使用以下内容才能成功读取大型CSV文件:
if (!ini_get('auto_detect_line_endings')) { ini_set('auto_detect_line_endings', '1'); }
请参阅文档的这一部分 。
我知道您正在使用php artisan serve
来运行服务器。 您可以尝试部署某种形式的实际Web服务器,因为您将在生产环境中使用它。 您可以尝试Apache,在Windows和Linux的XAMPP中很容易获得。
您可以在线检查如何在操作系统上安装Apache HTTP Server或Nginx。 这些具有更好的控制和使用php默认服务器的内存。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.