首先我们要知道的就是excel与csv区别,常规都是导入导出excel文件的,但是随着文件的增大,内存的消耗以及解析与生成的速率等等,后来逐步采用了csv来替代excel。
下面来说一下PHP如何解析大文件csv以及如何生成csv文件,我来说一下个人的观点:
1.首先解析大文件csv,因为是大文件所以我们要限制它的大小,两层限制,限制解析文件的大小,限制读取的行数,其中我们会用到两个函数:fgetcsv与fgets
下面来说一下这两个函数:
①fgets() 函数从文件指针中读取一行。
fgets(file,length)
参数 | 描述 |
file | 必需。规定要读取的文件。 |
length | 可选。规定要读取的字节数。默认是 1024 字节。 |
②fgetcsv() 函数从文件指针中读入一行并解析 CSV 字段。
与
fgets() 类似,不同的是 fgetcsv() 解析读入的行并找出 CSV 格式的字段,然后返回一个包含这些字段的数组。
fgetcsv() 出错时返回 FALSE,包括碰到文件结束时。
注释:从 PHP 4.3.5 起,fgetcsv() 的操作是二进制安全的。
fgetcsv(file,length,separator,enclosure)
参数 | 描述 |
file | 必需。规定要检查的文件。 |
length | 可选。规定行的最大长度。必须大于 CVS 文件内最长的一行。 在 PHP 5 中该参数是可选的。在 PHP 5 之前是必需的。 如果忽略(在 PHP 5.0.4 以后的版本中设为 0)该参数的话,那么长度就没有限制,不过可能会影响执行效率。 |
separator | 可选。设置字段分界符(只允许一个字符),默认值为逗号。 |
enclosure | 可选。设置字段环绕符(只允许一个字符),默认值为双引号。 该参数是在 PHP 4.3.0 中添加的。 |
下面是所封装的方法:
/**
* import
读取
CSV
文件中的某几行数据
*
@param
$csvfile csv
文件路径
*
@param
$lines
读取行数
*
@param
$offset
起始行数
*
@return
array
* */
public static function
import
(
$csvfile
,
$lines
=
0
,
$offset
=
0
) {
if
(!
$fp
= fopen(
$csvfile
,
'r'
)) {
return false
;
}
$arr
= explode(
'.'
,
$csvfile
)
;
if
(
$arr
[
1
] !=
'csv'
){
echo
'
该文件不是
csv
文件,请导入
csv
文件
'
;
exit
;
}
if
(filesize(
$csvfile
) >
4194304
){
echo
'
该
csv
文件不能大于
4M'
;
exit
;
}
$i
=
$j
=
0
;
while
(
false
!== (
$line
= fgets(
$fp
))) {
if
(
$i
++ <
$offset
) {
continue
;
}
break
;
}
$data
=
array
()
;
if
(
$lines
==
0
){
$lines
= count(file(
$csvfile
))
;
}
while
((
$j
++ <
$lines
) && !feof(
$fp
)) {
$data
[] = fgetcsv(
$fp
)
;
}
fclose(
$fp
)
;
foreach
(
$data
as
$key
=>
$val
){
if
(
empty
(
$val
)){
unset
(
$data
[
$key
])
;
}
}
return
$data
;
}
2.大文件csv生成,打包成压缩包供下载:
因为时间问题,暂无法解答,代码如下
/**
*
输出
UTF-8
编码的
csv
文件
*
@param
array $head
标题
*
@param
$data
数据
*
@param
string $mark
文件名(压缩包内)
*
@param
string $fileName
文件名(一个文件)
*/
public static function
export
(
array
$head
,
$data
,
$mark
=
'meeboo_statistics_info'
,
$fileName
=
"info.csv"
){
set_time_limit(
0
)
;
$sqlCount
= count(
$data
)
;
//
输出
Excel
文件头,可把
user.csv
换成你要的文件名
header(
'Content-Type: application/vnd.ms-excel;charset=utf-8'
)
;
header(
'Content-Disposition: attachment;filename="'
.
$fileName
.
'"'
)
;
header(
'Cache-Control: max-age=0'
)
;
$sqlLimit
=
30000
;
//
每次只从数据库取
30000
条以防变量缓存太大
//
每隔
$limit
行,刷新一下输出
buffer
,不要太大,也不要太小
$limit
=
30000
;
// buffer
计数器
$cnt
=
0
;
$fileNameArr
=
array
()
;
//
逐行取出数据,不浪费内存
for
(
$i
=
0
;
$i
< ceil(
$sqlCount
/
$sqlLimit
)
;
$i
++) {
$fp
= fopen(
$mark
.
'_'
.
$i
.
'.csv'
,
'w'
)
;
//
生成临时文件
$fileNameArr
[] =
$mark
.
'_'
.
$i
.
'.csv'
;
//
将数据通过
fputcsv
写到文件句柄
fputcsv
(
$fp
,
$head
)
;
$dataArr
=
array
()
;
$arr
=
$data
;
if
(
$i
==
0
){
foreach
(
$arr
as
$key
=>
$val
){
if
(
$key
>
$sqlLimit
-
1
){
unset
(
$arr
[
$key
])
;
}
$dataArr
=
$arr
;
}
}
else
{
foreach
(
$data
as
$k
=>
$v
){
if
(
$k
<=(
$sqlLimit
*(
$i
)-
1
) ||
$k
>(
$sqlLimit
*(
$i
+
1
)-
1
)){
unset
(
$data
[
$k
])
;
}
$dataArr
=
$data
;
}
}
foreach
(
$dataArr
as
$a
) {
$cnt
++
;
if
(
$limit
==
$cnt
) {
//
刷新一下输出
buffer
,防止由于数据过多造成问题
ob_flush()
;
flush()
;
$cnt
=
0
;
}
fputcsv
(
$fp
,
$a
)
;
}
fclose(
$fp
)
;
//
每生成一个文件关闭
}
//
进行多个文件压缩
$zip
=
new
\ZipArchive()
;
$filename
=
$mark
.
".zip"
;
$zip
->
open
(
$filename
,
\ZipArchive::
CREATE
)
;
//
打开压缩包
foreach
(
$fileNameArr
as
$file
) {
$zip
->
addFile
(
$file
,
basename(
$file
))
;
//
向压缩包中添加文件
}
$zip
->
close
()
;
//
关闭压缩包
foreach
(
$fileNameArr
as
$file
) {
unlink(
$file
)
;
//
删除
csv
临时文件
}
//
下载
self
::
export_csv
(
$filename
)
;
}
/**
* //
输出压缩文件提供下载
*
@param
$filename
*/
private static function
export_csv
(
$filename
){
header(
"Cache-Control: max-age=0"
)
;
header(
"Content-Description: File Transfer"
)
;
header(
'Content-disposition: attachment; filename='
. basename(
$filename
))
;
//
文件名
header(
"Content-Type: application/zip"
)
;
// zip
格式的
header(
"Content-Transfer-Encoding: binary"
)
;
header(
'Content-Length: '
. filesize(
$filename
))
;
@readfile(
$filename
)
;
//
输出文件
;
unlink(
$filename
)
;
//
删除压缩包临时文件
}
微信扫描二维码,关注我的公众号,一起讨论技术,共勉。