前言
网上有很多介绍大量上传数据的,感觉都是一个抄一个,这是自己写的处理方式,在一些项目中已经应用。
主要利用 yield 完成文件读取,这个重点看会了,其他基本就很简单。.
代码部分
一. controller 写法
//获取请求的参数
$arrayParams = Request::all();
$objCsvFile = $arrayParams['csv_file'];
$strRealPath = $objCsvFile->getRealPath();//tmp路径, 这里可以先保存到自己预定路径,再进行读取
//**************重点在这一步********************//
$glob = CommonUtilFunction::readPathCsvFile($strRealPath);
//********************************************//
$intRowNum = 0;
while($glob->valid()) {
$arrayNewLineData = [];
$intRowNum++;
if (1 === $intRowNum) {
//第一行跳过,一般是标题
$glob->next();
continue;
}
$arrayLineData = $glob->current();
//处理空字符串 空行
/**
* 一般csv有两种行数据可以被认为是空行
* 第一种 ',,,,,,,,,,,,,,,,,,,,,,,,,,',类似这种纯逗号没有任何数据
* 第二种 ' ',是真的空行,什么也没有
* 处理完成返回一个统一的数组 []
*/
$arrayLineData = CommonUtilFunction::dealCsvLineData($arrayLineData);
//跳过空行
if (true === empty($arrayLineData)) {
$glob->next();
continue;
}
//自己的代码逻辑
...
// 避免意外错误
unset($arrayNewLineData);
$glob->next(); // 处理下一行数据
}
二. yield 读取数据以及处理空行方法
/**
* @description 迭代器读取csv文件
* @param $strCsvPath
* @return \Generator
*/
public static function readPathCsvFile($strCsvPath)
{
if ($handle = fopen($strCsvPath, 'r')) {
while (!feof($handle)) {
yield fgetcsv($handle);
}
fclose($handle);
}
}
/**
* @description 处理c单行信息
* @param $arrData
* @return \Generator
*/public static function dealCsvLineData($arrData = [])
{
$arrAfterData = [];
if (false === empty($arrData)) {
//去除每个字符串 前后空格
foreach ($arrData as &$colData) {
//检测对应编码格式 csv文件格式Shift-JIS
$strEncodeType = mb_detect_encoding($colData, ['UTF-8', 'Shift-JIS']);
//如果认为utf-8格式不用转码, shift-jis格式需要转为utf8格式
if ('SJIS' === $strEncodeType) {
//jis=>utf8
$colData = mb_convert_encoding($colData, 'UTF-8', 'Shift-JIS');
}
$colData = trim($colData);
}
//去除空行
$isEmptyRow = true;
foreach ($arrData as $item) {
if ('' !== $item) {
$isEmptyRow = false;
break;
}
}
if (false === $isEmptyRow) {
$arrAfterData = $arrData;
}
}
return $arrAfterData;
}
结论
使用 yield 可以很大程度上减低服务器开销,压力在数据库方面。上限没有测试过,不过 1 万条数据是很轻松。