ExcelUtil.php 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. <?php
  2. namespace app\common\util;
  3. use PhpOffice\PhpSpreadsheet\IOFactory;
  4. use PhpOffice\PhpSpreadsheet\Spreadsheet;
  5. use PhpOffice\PhpSpreadsheet\Style\Alignment;
  6. class ExcelUtil
  7. {
  8. /**
  9. * 获取导入文件数据
  10. * @param string $filepath 文件路径
  11. * @param array $cols 数据列表名 ['id','name','class']
  12. * @param int $row 数据从第几行开始取
  13. * @return array|bool
  14. * @throws \PhpOffice\PhpSpreadsheet\Reader\Exception
  15. */
  16. static public function read($filepath = '', $cols = [],$row = 2){
  17. $e = explode('.',$filepath);
  18. $ext = $e[count($e)-1];
  19. if($ext == 'xls'){
  20. $objread = IOFactory::createReader('Xls');
  21. }else{
  22. $objread = IOFactory::createReader('Xlsx');
  23. }
  24. $newArr=['xls','xlsx'];
  25. if(!in_array($ext,$newArr)){
  26. return false;
  27. }
  28. $objspreadsheet = $objread->load($filepath);
  29. $ret = $objspreadsheet->getActiveSheet()->toArray();
  30. $ret = $ret?$ret:[];
  31. $nret = [];
  32. foreach ($ret as $k=>$v){
  33. if($k + 1 < $row){
  34. continue;
  35. }
  36. if($cols){
  37. foreach ($cols as $kk=>$vv){
  38. $v[$kk] = str_ireplace(',','',$v[$kk]);
  39. $v[$kk] = str_ireplace(' ','',$v[$kk]);
  40. if($vv == 'name'){
  41. $a[$vv] = trim($v[$kk]);
  42. }else{
  43. $a[$vv] = trim($v[$kk])?floatval(trim($v[$kk])):0;
  44. }
  45. }
  46. $nret[] = $a;
  47. }else{
  48. $nret[] = $v;
  49. }
  50. }
  51. return $nret;
  52. }
  53. /**
  54. * 普通数据导出
  55. * @param $filename 导出文件名
  56. * @param array $header 头数据
  57. * @param array $data 导出数据,数据过多会分散到多sheet中,数据过大注意响应时间和内存
  58. * @param string $savepath 保存路径,直接输出不用填
  59. * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception
  60. */
  61. static public function export($filename,$header = [],$data = [],$savepath = ''){
  62. // $header = [ // header格式,title和name必须存在
  63. // ['title' => '编号', 'name' => 'id','width'=>'20'],
  64. // ['title' => '姓名', 'name' => 'name','width'=>'20'],
  65. // ['title' => '班级', 'name' => 'class','width'=>'20'],
  66. // ];
  67. $spreadsheet = new Spreadsheet();
  68. $worksheet = $spreadsheet->getActiveSheet();
  69. $worksheet->setTitle($filename);
  70. $widths = [];
  71. $titles = [];
  72. $names = [];
  73. foreach ($header as $k=>$v){
  74. $width = isset($v['width'])&&$v['width'] > 0 ?$v['width']:20;
  75. $widths[] = $width;
  76. $titles[] = isset($v['title'])?$v['title']:'';
  77. $names[] = $v['name'];
  78. }
  79. $char = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'];
  80. $lins = [];
  81. for( $i=0;$i<count($header);$i++ ){
  82. $r1 = $i%26;
  83. $r2 = floor($i/26);
  84. if($r2 == 0){
  85. $lins[] = $char[$r1];
  86. }else if($r2 == 1){
  87. $lins[] = 'A'.$char[$r1];
  88. }else if($r2 == 2){
  89. $lins[] = 'B'.$char[$r1];
  90. }else if($r2 == 3){
  91. $lins[] = 'C'.$char[$r1];
  92. }else if($r2 == 4){
  93. $lins[] = 'D'.$char[$r1];
  94. }else if($r2 == 5){
  95. $lins[] = 'E'.$char[$r1];
  96. }else if($r2 == 6){
  97. $lins[] = 'F'.$char[$r1];
  98. }
  99. }
  100. $lists = self::chunkData($data);
  101. foreach ($lists as $k=>$v){
  102. if($k > 0){
  103. $sheet = $spreadsheet->createSheet($k+1)->setTitle($filename.($k+1));
  104. }else{
  105. $sheet = $worksheet;
  106. }
  107. self::writeSheet($sheet,$v,$titles,$widths,$lins,$names);
  108. }
  109. if(!$lists){
  110. self::writeSheet($worksheet,[],$titles,$widths,$lins,$names);
  111. }
  112. $writer = \PhpOffice\PhpSpreadsheet\IOFactory::createWriter($spreadsheet, 'Xlsx');
  113. if($savepath){
  114. $res = $writer->save($savepath);
  115. }else{
  116. $path = $filename.'_'.date('YmdHis').'.xlsx';
  117. header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
  118. header('Content-Disposition: attachment;filename="'.$path.'"');
  119. header('Cache-Control: max-age=0');
  120. $writer->save('php://output');
  121. }
  122. }
  123. // 数据分段
  124. static private function chunkData($data,$length = 50000){
  125. $ret = [];
  126. $arr = [];
  127. foreach ($data as $k=>$v){
  128. $arr[] = $v;
  129. if(count($arr) == $length){
  130. $ret[] = $arr;
  131. $arr = [];
  132. }
  133. }
  134. if($arr){
  135. $ret[] = $arr;
  136. }
  137. return $ret;
  138. }
  139. /**
  140. * 多sheet导出,不用数据分段
  141. * @param $filename 导出文件名
  142. * @param array $header 头数据
  143. * @param array $sheets sheet名称数组
  144. * @param array $data 导出的二维数组
  145. * @param string $savepath 保存路径,直接导出不用填
  146. * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception
  147. */
  148. static public function exportBySheet($filename,$header = [],$sheets = [],$data = [],$savepath = ''){
  149. // $header = [ // header格式,title和name必须存在
  150. // ['title' => '编号', 'name' => 'id','width'=>'20'],
  151. // ['title' => '姓名', 'name' => 'name','width'=>'20'],
  152. // ['title' => '班级', 'name' => 'class','width'=>'20'],
  153. // ];
  154. // $sheets = ['sheet1','sheet2'];
  155. // $data = [['id'=>1,'name'=>'aaa']]; 二维数组
  156. $spreadsheet = new Spreadsheet();
  157. $worksheet = $spreadsheet->getActiveSheet();
  158. $widths = [];
  159. $titles = [];
  160. $names = [];
  161. foreach ($header as $k=>$v){
  162. $width = isset($v['width'])&&$v['width'] > 0 ?$v['width']:20;
  163. $widths[] = $width;
  164. $titles[] = isset($v['title'])?$v['title']:'';
  165. $names[] = $v['name'];
  166. }
  167. $char = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'];
  168. $lins = [];
  169. for( $i=0;$i<count($header);$i++ ){
  170. $r1 = $i%26;
  171. $r2 = floor($i/26);
  172. if($r2 == 0){
  173. $lins[] = $char[$r1];
  174. }else if($r2 == 1){
  175. $lins[] = 'A'.$char[$r1];
  176. }else if($r2 == 2){
  177. $lins[] = 'B'.$char[$r1];
  178. }else if($r2 == 3){
  179. $lins[] = 'C'.$char[$r1];
  180. }else if($r2 == 4){
  181. $lins[] = 'D'.$char[$r1];
  182. }else if($r2 == 5){
  183. $lins[] = 'E'.$char[$r1];
  184. }else if($r2 == 6){
  185. $lins[] = 'F'.$char[$r1];
  186. }
  187. }
  188. $lists = $data;
  189. foreach ($lists as $k=>$v){
  190. if($k > 0){
  191. $sheet = $spreadsheet->createSheet($k+1)->setTitle($sheets[$k]);
  192. }else{
  193. $worksheet->setTitle($sheets[$k]);
  194. $sheet = $worksheet;
  195. }
  196. self::writeSheet($sheet,$v,$titles,$widths,$lins,$names);
  197. }
  198. if(!$lists){
  199. self::writeSheet($worksheet,[],$titles,$widths,$lins,$names);
  200. }
  201. $writer = \PhpOffice\PhpSpreadsheet\IOFactory::createWriter($spreadsheet, 'Xlsx');
  202. if($savepath){
  203. $res = $writer->save($savepath);
  204. }else{
  205. $path = $filename.'_'.date('YmdHis').'.xlsx';
  206. header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
  207. header('Content-Disposition: attachment;filename="'.$path.'"');
  208. header('Cache-Control: max-age=0');
  209. $writer->save('php://output');
  210. }
  211. }
  212. // 按sheet写入数据
  213. static private function writeSheet($sheet,$data,$titles,$widths,$lins,$names){
  214. foreach ($titles as $key => $value) {
  215. $sheet->setCellValueByColumnAndRow($key+1, 1, $value);
  216. }
  217. $styleArray = [
  218. 'font' => [
  219. 'bold' => true
  220. ],
  221. 'alignment' => [
  222. 'horizontal' => \PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER,
  223. ],
  224. ];
  225. $styleArray2 = [
  226. 'alignment' => [
  227. 'horizontal' => \PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER,
  228. ],
  229. ];
  230. foreach ($lins as $k=>$v){
  231. $sheet->getStyle($v.'1')->applyFromArray($styleArray)->getFont()->setSize(12);
  232. $sheet->getStyle($v)->applyFromArray($styleArray2)->getAlignment()->setWrapText(true);//换行
  233. $sheet->getColumnDimension($v)->setWidth($widths[$k]);
  234. }
  235. foreach ($data as $k=>$v){
  236. foreach ($names as $kk=>$vv){
  237. $sheet->setCellValueByColumnAndRow($kk+1, $k + 2, self::filterEmoji($v[$vv]));
  238. }
  239. }
  240. }
  241. // 过滤表情
  242. static private function filterEmoji($str){
  243. $str = preg_replace_callback('/./u',function ($match){
  244. return strlen($match[0]) >= 4?'':$match[0];
  245. },$str);
  246. return $str;
  247. }
  248. }