主要參考最後兩個方法
<?php
namespace App\Http\Controllers;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use ZipArchive;
class TestController extends Controller
{
/**
* 忽略
* PS:
* 1.mb_convert_encoding,iconv 可能會導致內存溢出
* 2.使用fputcsv()
*/
public function indexTwo()
{
set_time_limit(0);
Log::info("IndexTwo起始時間:" . date('Y-m-d H:i:s', time()));
header('Content-Type: application/vnd.ms-excel');
header('Content-Disposition: attachment;filename=2.csv');
header('Cache-Control: max-age=0');
$myfile = fopen('php://output', 'a');
$headlist = [
'編號1',
'編號2',
'編號3'
];
//輸出Excel列名信息
foreach ($headlist as $key => $value) {
//CSV的Excel支持GBK編碼,一定要轉換,否則亂碼
$headlist[$key] = iconv('UTF-8', 'GBK', $value);
}
//將數據通過fputcsv寫到文件句柄
fputcsv($myfile, $headlist);
$select = [
"orders.*",
"order_senders.sender_name",
"order_senders.sender_phone",
"order_senders.sender_province_name",
"order_senders.sender_city_name",
"order_senders.sender_county_name",
"po.order_number as parent_order_number",
"order_senders.sender_street_name",
"order_senders.sender_detail",
"orders.order_status",
"order_expresses.express_company_name",
"order_expresses.weight_from_express",
"order_expresses.freight",
"order_expresses.remark_from_express",
"order_senders.order_id",
"order_expresses.express_number",
"order_expresses.express_company_code",
"warehouses.warehouse_name",
"order_expresses.id as order_expressesId",
DB::raw("( SELECT GROUP_CONCAT( order_waybills.warehouse_signed_at ) FROM order_waybills WHERE order_expresses.id = order_waybills.order_express_id ) AS waybill_signCreatedAt"),
DB::raw("( SELECT GROUP_CONCAT( order_waybills.tracking_number ) FROM order_waybills WHERE order_expresses.id = order_waybills.order_express_id ) AS tracking_number"),
DB::raw("( SELECT GROUP_CONCAT( order_waybills.express_signed_at ) FROM order_waybills WHERE order_expresses.id = order_waybills.order_express_id) AS express_signed_at"),
];
$query = DB::table('orders')
->leftJoin('order_expresses', 'orders.id', '=', 'order_expresses.order_id')
->leftJoin('order_senders', 'orders.id', '=', 'order_senders.order_id')
->leftJoin('orders as po', 'po.id', '=', 'orders.parent_id')
->leftJoin('order_receivers', 'orders.id', '=', 'order_receivers.order_id')
->leftJoin('warehouses', 'order_receivers.warehouse_code', '=', 'warehouses.warehouse_code')
->orderBy('orders.created_at', 'desc');
$query->where('orders.created_at', '>', '2019-1-23')
->where('orders.created_at', '<', '2019-4-10')
->select($select)->chunk(20000, function ($terminal) use (&$myfile) {
$data = [];
foreach ($terminal as $key => $value) {
$data['sender_name'] = mb_convert_encoding($value->sender_name, 'GBK', 'utf-8');
$data['sender_phone'] = mb_convert_encoding($value->sender_phone, 'GBK', 'utf-8');
$data['sender_province_name'] = mb_convert_encoding($value->sender_province_name, 'GBK', 'utf-8');
$data['sender_city_name'] = mb_convert_encoding($value->sender_city_name, 'GBK', 'utf-8');
$data['sender_county_name'] = mb_convert_encoding($value->sender_county_name, 'GBK', 'utf-8');
$data['parent_order_number'] = mb_convert_encoding($value->parent_order_number, 'GBK', 'utf-8');
$data['sender_street_name'] = mb_convert_encoding($value->sender_street_name, 'GBK', 'utf-8');
$data['sender_detail'] = mb_convert_encoding($value->sender_detail, 'GBK', 'utf-8');
$data['order_status'] = mb_convert_encoding($value->order_status, 'GBK', 'utf-8');
$data['express_company_name'] = mb_convert_encoding($value->express_company_name, 'GBK', 'utf-8');
$data['weight_from_express'] = mb_convert_encoding($value->weight_from_express, 'GBK', 'utf-8');
$data['freight'] = mb_convert_encoding($value->freight, 'GBK', 'utf-8');
$data['remark_from_express'] = mb_convert_encoding($value->remark_from_express, 'GBK', 'utf-8');
$data['order_id'] = mb_convert_encoding($value->order_id, 'GBK', 'utf-8');
$data['express_number'] = mb_convert_encoding($value->express_number, 'GBK', 'utf-8');
$data['express_company_code'] = mb_convert_encoding($value->express_company_code, 'GBK', 'utf-8');
$data['warehouse_name'] = mb_convert_encoding($value->warehouse_name, 'GBK', 'utf-8');
$data['order_expressesId'] = mb_convert_encoding($value->order_expressesId, 'GBK', 'utf-8');
$data['waybill_signCreatedAt'] = mb_convert_encoding($value->waybill_signCreatedAt, 'GBK', 'utf-8');
$data['tracking_number'] = mb_convert_encoding($value->tracking_number, 'GBK', 'utf-8');
$data['express_signed_at'] = mb_convert_encoding($value->express_signed_at, 'GBK', 'utf-8');
fputcsv($myfile, $data);
}
unset($data);
});
fclose($myfile);
Log::info("IndexTwo截止時間:" . date('Y-m-d H:i:s', time()));
}
/**
* 略
* 使用 offset limit 獲取數據
* PS:多次使用mb_convert_encoding,可能導致內存泄漏
*/
public function indexFour()
{
set_time_limit(0);
header('Content-Encoding: UTF-8');
header("Content-type:application/vnd.ms-excel;charset=UTF-8");
header('Content-Disposition: attachment;filename=3.csv');
//打開php標準輸出流以寫入追加的方式打開
$fp = fopen('php://output', 'a');
//用fputcsv從數據庫中導出1百萬的數據,比如我們每次取1萬條數據,分100步來執行
$nums = 10000;
//設置標題
$title = array('id', '編號', '姓名', '年齡'); //注意這裏是小寫id,否則ID命名打開會提示Excel 已經檢測到"xxx.xsl"是SYLK文件,但是不能將其加載: CSV 文或者XLS文件的前兩個字符是大寫字母"I","D"時,會發生此問題。
foreach ($title as $key => $item)
$title[$key] = iconv("UTF-8", "GB2312//IGNORE", $item);
fputcsv($fp, $title);
// 獲取數據
$select = [
"orders.*",
"order_senders.sender_name",
"order_senders.sender_phone",
"order_senders.sender_province_name",
"order_senders.sender_city_name",
"order_senders.sender_county_name",
"po.order_number as parent_order_number",
"order_senders.sender_street_name",
"order_senders.sender_detail",
"orders.order_status",
"order_expresses.express_company_name",
"order_expresses.weight_from_express",
"order_expresses.freight",
"order_expresses.remark_from_express",
"order_senders.order_id",
"order_expresses.express_number",
"order_expresses.express_company_code",
"warehouses.warehouse_name",
"order_expresses.id as order_expressesId",
DB::raw("( SELECT GROUP_CONCAT( order_waybills.warehouse_signed_at ) FROM order_waybills WHERE order_expresses.id = order_waybills.order_express_id ) AS waybill_signCreatedAt"),
DB::raw("( SELECT GROUP_CONCAT( order_waybills.tracking_number ) FROM order_waybills WHERE order_expresses.id = order_waybills.order_express_id ) AS tracking_number"),
DB::raw("( SELECT GROUP_CONCAT( order_waybills.express_signed_at ) FROM order_waybills WHERE order_expresses.id = order_waybills.order_express_id) AS express_signed_at"),
];
$query = DB::table('orders')
->select($select)
->leftJoin('order_expresses', 'orders.id', '=', 'order_expresses.order_id')
->leftJoin('order_senders', 'orders.id', '=', 'order_senders.order_id')
->leftJoin('orders as po', 'po.id', '=', 'orders.parent_id')
->leftJoin('order_receivers', 'orders.id', '=', 'order_receivers.order_id')
->leftJoin('warehouses', 'order_receivers.warehouse_code', '=', 'warehouses.warehouse_code')
->where('orders.created_at', '>', '2019-3-28')
->where('orders.created_at', '<', '2019-3-29')
->orderBy('orders.created_at', 'desc');
// 總記錄數
$count = $query->count();
// 分批次數
$step = ceil($count / $nums);
for ($s = 1; $s <= $step; $s++) {
$start = ($s - 1) * $nums;
$data = $query->offset($start)->limit($nums)->get();
foreach ($data as $key => $item) {
$tmp = [];
$tmp['sender_name'] = mb_convert_encoding($item->sender_name, 'GBK', 'utf-8');
$tmp['sender_phone'] = mb_convert_encoding($item->sender_phone, 'GBK', 'utf-8');
$tmp['sender_province_name'] = mb_convert_encoding($item->sender_province_name, 'GBK', 'utf-8');
$tmp['sender_city_name'] = mb_convert_encoding($item->sender_city_name, 'GBK', 'utf-8');
$tmp['sender_county_name'] = mb_convert_encoding($item->sender_county_name, 'GBK', 'utf-8');
$tmp['parent_order_number'] = mb_convert_encoding($item->parent_order_number, 'GBK', 'utf-8');
$tmp['sender_street_name'] = mb_convert_encoding($item->sender_street_name, 'GBK', 'utf-8');
$tmp['sender_detail'] = mb_convert_encoding($item->sender_detail, 'GBK', 'utf-8');
$tmp['order_status'] = mb_convert_encoding($item->order_status, 'GBK', 'utf-8');
$tmp['express_company_name'] = mb_convert_encoding($item->express_company_name, 'GBK', 'utf-8');
$tmp['weight_from_express'] = mb_convert_encoding($item->weight_from_express, 'GBK', 'utf-8');
$tmp['freight'] = mb_convert_encoding($item->freight, 'GBK', 'utf-8');
$tmp['remark_from_express'] = mb_convert_encoding($item->remark_from_express, 'GBK', 'utf-8');
$tmp['order_id'] = mb_convert_encoding($item->order_id, 'GBK', 'utf-8');
$tmp['express_number'] = mb_convert_encoding($item->express_number, 'GBK', 'utf-8');
$tmp['express_company_code'] = mb_convert_encoding($item->express_company_code, 'GBK', 'utf-8');
$tmp['warehouse_name'] = mb_convert_encoding($item->warehouse_name, 'GBK', 'utf-8');
$tmp['order_expressesId'] = mb_convert_encoding($item->order_expressesId, 'GBK', 'utf-8');
$tmp['waybill_signCreatedAt'] = mb_convert_encoding($item->waybill_signCreatedAt, 'GBK', 'utf-8');
$tmp['tracking_number'] = mb_convert_encoding($item->tracking_number, 'GBK', 'utf-8');
$tmp['express_signed_at'] = mb_convert_encoding($item->express_signed_at, 'GBK', 'utf-8');
fputcsv($fp, $tmp);
}
unset($data);
ob_flush(); //每1萬條數據就刷新緩衝區
flush();
}
fclose($fp);
}
/**
* 分片查詢寫入文件 合併 壓縮後導出
*/
public function indexFive()
{
Log::info("起始時間:" . date('Y-m-d H:i:s', time()));
set_time_limit(0);
$select = [
"orders.*",
"order_senders.sender_name",
"order_senders.sender_phone",
"order_senders.sender_province_name",
"order_senders.sender_city_name",
"order_senders.sender_county_name",
"po.order_number as parent_order_number",
"order_senders.sender_street_name",
"order_senders.sender_detail",
"orders.order_status",
"order_expresses.express_company_name",
"order_expresses.weight_from_express",
"order_expresses.freight",
"order_expresses.remark_from_express",
"order_senders.order_id",
"order_expresses.express_number",
"order_expresses.express_company_code",
"warehouses.warehouse_name",
"order_expresses.id as order_expressesId",
DB::raw("( SELECT GROUP_CONCAT( order_waybills.warehouse_signed_at ) FROM order_waybills WHERE order_expresses.id = order_waybills.order_express_id ) AS waybill_signCreatedAt"),
DB::raw("( SELECT GROUP_CONCAT( order_waybills.tracking_number ) FROM order_waybills WHERE order_expresses.id = order_waybills.order_express_id ) AS tracking_number"),
DB::raw("( SELECT GROUP_CONCAT( order_waybills.express_signed_at ) FROM order_waybills WHERE order_expresses.id = order_waybills.order_express_id) AS express_signed_at"),
];
$query = DB::table('orders')
->select($select)
->leftJoin('order_expresses', 'orders.id', '=', 'order_expresses.order_id')
->leftJoin('order_senders', 'orders.id', '=', 'order_senders.order_id')
->leftJoin('orders as po', 'po.id', '=', 'orders.parent_id')
->leftJoin('order_receivers', 'orders.id', '=', 'order_receivers.order_id')
->leftJoin('warehouses', 'order_receivers.warehouse_code', '=', 'warehouses.warehouse_code')
->where('orders.created_at', '>', '2019-1-23')
->where('orders.created_at', '<', '2019-4-10')
->orderBy('orders.created_at', 'desc');
// 總記錄數
$sqlCount = $query->count();
// 單個Excel記錄條數
$sqlLimit = 100000;
// 循環記錄初始值
$cnt = 0;
// 分割文件名數組
$fileNameArr = [];
// 分割文件名前綴標識
$mark = "mark";
// Excel列頭信息
$headArr = ['編號', '編號', '編號', '編號', '編號', '編號', '編號', '編號', '編號', '編號', '編號', '編號', '編號', '編號', '編號'];
// 檢測Excel臨時文件夾是否存在(按日期生成目錄)
$fileTmpDir = public_path('Export/FileTmp/' . date('Ymd'));
if (!is_dir($fileTmpDir))
mkdir($fileTmpDir, 0777, true);
$bom = chr(0xEF) . chr(0xBB) . chr(0xBF);
// 寫入數據
for ($i = 0; $i < ceil($sqlCount / $sqlLimit); $i++) {
$fileName = $fileTmpDir . '/' . $mark . "_" . microtime(true) . '_' . $i . ".csv";
$fp = fopen($fileName, "w");
// 輸出Excel列頭信息
foreach ($headArr as $key => $value) {
if ($key == 0) {
$headArr[$key] = $bom . $value; // 添加Bom頭解決亂碼問題
} else {
$headArr[$key] = $value;
}
}
fputcsv($fp, $headArr);
// 保存文件名稱
$fileNameArr[] = $fileName;
// 獲取分片數據
$dataArr = $query->offset($i * $sqlLimit)->limit($sqlLimit)->get()->toArray();
// 數據處理
foreach ($dataArr as $item) {
$item = json_decode(json_encode($item), true);
$tmpItemArr = []; // 數據編碼處理
$tmpItemArr['sender_name'] = $bom . $item['sender_name'];
$tmpItemArr['sender_phone'] = $item['sender_phone'];
$tmpItemArr['sender_province_name'] = $item['sender_province_name'];
$tmpItemArr['sender_city_name'] = $item['sender_city_name'];
$tmpItemArr['sender_county_name'] = $item['sender_county_name'];
$tmpItemArr['parent_order_number'] = $item['parent_order_number'];
$tmpItemArr['sender_street_name'] = $item['sender_street_name'];
$tmpItemArr['sender_detail'] = $item['sender_detail'];
$tmpItemArr['order_status'] = $item['order_status'];
$tmpItemArr['express_company_name'] = $item['express_company_name'];
$tmpItemArr['weight_from_express'] = $item['weight_from_express'];
$tmpItemArr['freight'] = $item['freight'];
$tmpItemArr['remark_from_express'] = $item['remark_from_express'];
$tmpItemArr['order_id'] = $item['order_id'];
$tmpItemArr['express_number'] = $item['express_number'];
$tmpItemArr['express_company_code'] = $item['express_company_code'];
$tmpItemArr['warehouse_name'] = $item['warehouse_name'];
$tmpItemArr['order_expressesId'] = $item['order_expressesId'];
$tmpItemArr['waybill_signCreatedAt'] = $item['waybill_signCreatedAt'];
$tmpItemArr['tracking_number'] = $item['tracking_number'];
$tmpItemArr['express_signed_at'] = $item['express_signed_at'];
$cnt++;
if ($cnt == $sqlLimit) {
ob_flush();
flush();
$cnt = 0;
}
fputcsv($fp, $tmpItemArr);
unset($tmpItemArr);
}
fclose($fp);
}
// 生成Zip包並導出
$zip = new \ZipArchive();
$zipName = $mark . '_' . microtime(true) . '.zip';
// 檢測Zip文件夾是否存在(按日期生成目錄)
$zipTmpDir = public_path('Export/ZipTmp/' . date('Ymd'));
if (!is_dir($zipTmpDir))
mkdir(iconv("UTF-8", "GBK", $zipTmpDir), 0777, true);
// 打開壓縮包
$zip->open($zipTmpDir . '/' . $zipName, ZipArchive::CREATE);
foreach ($fileNameArr as $fileNameValue) {
$zip->addFile($fileNameValue, basename($fileNameValue));
}
$zip->close();
// 刪除Excel臨時文件
foreach ($fileNameArr as $fileNameValue) {
unlink($fileNameValue);
}
header('Cache-Control: max-age=0');
header('Content-Description: File Transfer');
header('Content-Disposition: attachment;filename=' . $zipName);
header('Content-type: application/zip');
header('Content-Transfer-Encoding: binary');
header('Content-Length: ' . filesize($zipTmpDir . '/' . $zipName));
@readfile($zipTmpDir . '/' . $zipName);
unlink($zipTmpDir . '/' . $zipName);
Log::info("截止時間:" . date('Y-m-d H:i:s', time()));
}
/**
* 導出CSV(分片查詢寫入文件)
*/
public function indexSix()
{
set_time_limit(0);
Log::info("起始時間:" . date('Y-m-d H:i:s', time()));
// 保存目錄
$dir_path = public_path('Six');
if (!is_dir($dir_path))
mkdir($dir_path, 0777, true);
// 文件名
$fileName = microtime(true) . '.csv';
// 檢測文件是否已存在
if (file_exists($dir_path . '/' . $fileName))
unlink($dir_path . '/' . $fileName);
// 打開文件
$myfile = fopen($dir_path . '/' . $fileName, "w") or die("Unable to open file!");
// Excel列表頭
$head = "\""
. '編號1' . "\",\""
. '編號2' . "\",\""
. '編號3' . "\",\""
. '編號4' . "\",\""
. '編號5' . "\",\""
. '編號6' . "\",\""
. '編號7' . "\",\""
. '編號8' . "\",\""
. '編號9' . "\",\""
. '編號10' . "\",\""
. '編號11' . "\",\""
. '編號12' . "\",\""
. '編號13'
. "\"\n";
$head = mb_convert_encoding($head, 'GBK', 'utf-8');
// 寫入頭部信息
fwrite($myfile, $head);
// 寫入內容
$select = [
"orders.*",
"order_senders.sender_name",
"order_senders.sender_phone",
"order_senders.sender_province_name",
"order_senders.sender_city_name",
"order_senders.sender_county_name",
"po.order_number as parent_order_number",
"order_senders.sender_street_name",
"order_senders.sender_detail",
"orders.order_status",
"order_expresses.express_company_name",
"order_expresses.weight_from_express",
"order_expresses.freight",
"order_expresses.remark_from_express",
"order_senders.order_id",
"order_expresses.express_number",
"order_expresses.express_company_code",
"warehouses.warehouse_name",
"order_expresses.id as order_expressesId",
DB::raw("( SELECT GROUP_CONCAT( order_waybills.warehouse_signed_at ) FROM order_waybills WHERE order_expresses.id = order_waybills.order_express_id ) AS waybill_signCreatedAt"),
DB::raw("( SELECT GROUP_CONCAT( order_waybills.tracking_number ) FROM order_waybills WHERE order_expresses.id = order_waybills.order_express_id ) AS tracking_number"),
DB::raw("( SELECT GROUP_CONCAT( order_waybills.express_signed_at ) FROM order_waybills WHERE order_expresses.id = order_waybills.order_express_id) AS express_signed_at"),
];
$query = DB::table('orders')
->leftJoin('order_expresses', 'orders.id', '=', 'order_expresses.order_id')
->leftJoin('order_senders', 'orders.id', '=', 'order_senders.order_id')
->leftJoin('orders as po', 'po.id', '=', 'orders.parent_id')
->leftJoin('order_receivers', 'orders.id', '=', 'order_receivers.order_id')
->leftJoin('warehouses', 'order_receivers.warehouse_code', '=', 'warehouses.warehouse_code')
->orderBy('orders.created_at', 'desc');
$query->where('orders.created_at', '>', '2019-1-23')
->where('orders.created_at', '<', '2019-4-11')
->select($select)->chunk(20000, function ($terminal) use (&$myfile) {
foreach ($terminal as $key => $value) {
$txt = "\""
. $value->sender_name . "\t\",\""
. $value->sender_phone . "\t\",\""
. $value->sender_province_name . "\t\",\""
. $value->sender_city_name . "\t\",\""
. $value->sender_county_name . "\t\",\""
. $value->parent_order_number . "\t\",\""
. $value->sender_street_name . "\t\",\""
. $value->sender_detail . "\t\",\""
. $value->order_status . "\t\",\""
. $value->express_company_name . "\t\",\""
. $value->weight_from_express . "\t\",\""
. $value->freight . "\t\",\""
. $value->remark_from_express . "\t\",\""
. $value->order_id . "\t\",\""
. $value->express_number . "\t\",\""
. $value->express_company_code . "\t\",\""
. $value->warehouse_name . "\t\",\""
. $value->order_expressesId . "\t\",\""
. $value->waybill_signCreatedAt . "\t\",\""
. $value->tracking_number . "\t\",\""
. $value->express_signed_at . "\t\",\""
. "\t\"\n";
$txt = mb_convert_encoding($txt, 'GBK', 'utf-8');
fwrite($myfile, $txt);
unset($txt);
}
});
fclose($myfile);
// 瀏覽器下載
header('Cache-Control: max-age=0');
header('Content-Description: File Transfer');
header('Content-Disposition: attachment;filename=' . $fileName);
header('Content-type: application/vnd.ms-excel');
header('Content-Transfer-Encoding: binary');
header('Content-Length: ' . filesize($dir_path . '/' . $fileName));
@readfile($dir_path . '/' . $fileName);
// 刪除臨時文件
unlink($dir_path . '/' . $fileName);
Log::info("截止時間:" . date('Y-m-d H:i:s', time()));
}
}