ExcelHelper.php 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. <?php
  2. /**
  3. * lemocms
  4. * ============================================================================
  5. * 版权所有 2018-2027 lemocms,并保留所有权利。
  6. * 网站地址: https://www.lemocms.com
  7. * ----------------------------------------------------------------------------
  8. * 采用最新Thinkphp6实现
  9. * ============================================================================
  10. * Author: yuege
  11. * Date: 2019/9/26
  12. */
  13. namespace lemo\helper;
  14. use Exception;
  15. use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
  16. use PhpOffice\PhpSpreadsheet\Spreadsheet;
  17. use PhpOffice\PhpSpreadsheet\Writer\Html;
  18. use PhpOffice\PhpSpreadsheet\Writer\Xls;
  19. use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
  20. use PhpOffice\PhpSpreadsheet\Writer\Csv;
  21. class ExcelHelper
  22. {
  23. /**
  24. * 导出Excel
  25. *
  26. * @param array $list
  27. * @param array $header
  28. * @param string $filename
  29. * @param string $title
  30. * @return bool
  31. * @throws \PhpOffice\PhpSpreadsheet\Exception
  32. * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception
  33. */
  34. public static function exportData($data = [], $header = [], $filename = '', $suffix = '.xlsx')
  35. {
  36. $filename .= "_" . date("Y_m_d", time()) . $suffix;
  37. $spreadsheet = new Spreadsheet();
  38. $objPHPExcel = $spreadsheet->getActiveSheet();
  39. $key = ord("A"); // 设置表头
  40. foreach ($header as $v) {
  41. $colum = chr($key);
  42. $objPHPExcel->setCellValue($colum . '1', $v);
  43. $key += 1;
  44. }
  45. $column = 2;
  46. foreach ($data as $key => $rows) {
  47. // 行写入
  48. $span = ord("A");
  49. foreach ($rows as $keyName => $value) {
  50. // 列写入
  51. $objPHPExcel->setCellValue(chr($span) . $column, $value);
  52. $span++;
  53. }
  54. $column++;
  55. }
  56. //$fileName = iconv("utf-8", "gbk//IGNORE", $fileName); // 重命名表(UTF8编码不需要这一步)
  57. header('Content-Type: application/vnd.ms-excel');
  58. header('Content-Disposition: attachment;filename="' . $filename . $suffix);
  59. header('Cache-Control: max-age=0');
  60. $writer = new Xlsx($spreadsheet);
  61. $writer->save('php://output');
  62. //删除清空:
  63. $spreadsheet->disconnectWorksheets();
  64. unset($spreadsheet);
  65. exit;
  66. }
  67. /**
  68. * 导出的另外一种形式(不建议使用)
  69. *
  70. * @param array $list
  71. * @param array $header
  72. * @param string $filename
  73. * @return bool
  74. */
  75. public static function exportCsvData($list = [], $header = [], $filename = '')
  76. {
  77. if (!is_array($list) || !is_array($header)) {
  78. return false;
  79. }
  80. // 清除之前的错误输出
  81. ob_end_clean();
  82. ob_start();
  83. !$filename && $filename = time();
  84. $html = "\xEF\xBB\xBF";
  85. foreach ($header as $k => $v) {
  86. $html .= $v[0] . "\t ,";
  87. }
  88. $html .= "\n";
  89. if (!empty($list)) {
  90. $info = [];
  91. $size = ceil(count($list) / 500);
  92. for ($i = 0; $i < $size; $i++) {
  93. $buffer = array_slice($list, $i * 500, 500);
  94. foreach ($buffer as $k => $row) {
  95. $data = [];
  96. foreach ($header as $key => $value) {
  97. // 解析字段
  98. $realData = self::formatting($header[$key], trim(self::formattingField($row, $value[1])), $row);
  99. $data[] = str_replace(PHP_EOL, '', $realData);
  100. }
  101. $info[] = implode("\t ,", $data) . "\t ,";
  102. unset($data, $buffer[$k]);
  103. }
  104. }
  105. $html .= implode("\n", $info);
  106. }
  107. header("Content-type:text/csv");
  108. header("Content-Disposition:attachment; filename={$filename}.csv");
  109. echo $html;
  110. exit();
  111. }
  112. /**
  113. * 导入
  114. *
  115. * @param $filePath
  116. * @param int $startRow
  117. * @return array|mixed
  118. * @throws Exception
  119. * @throws \PhpOffice\PhpSpreadsheet\Exception
  120. * @throws \PhpOffice\PhpSpreadsheet\Reader\Exception
  121. */
  122. public static function import($filePath, $startRow = 1)
  123. {
  124. $reader = new \PhpOffice\PhpSpreadsheet\Reader\Xlsx();
  125. $reader->setReadDataOnly(true);
  126. if (!$reader->canRead($filePath)) {
  127. $reader = new \PhpOffice\PhpSpreadsheet\Reader\Xls();
  128. // setReadDataOnly Set read data only 只读单元格的数据,不格式化 e.g. 读时间会变成一个数据等
  129. $reader->setReadDataOnly(true);
  130. if (!$reader->canRead($filePath)) {
  131. throw new Exception('不能读取Excel');
  132. }
  133. }
  134. $spreadsheet = $reader->load($filePath);
  135. $sheetCount = $spreadsheet->getSheetCount();// 获取sheet的数量
  136. // 获取所有的sheet表格数据
  137. $excleDatas = [];
  138. $emptyRowNum = 0;
  139. for ($i = 0; $i < $sheetCount; $i++) {
  140. $currentSheet = $spreadsheet->getSheet($i); // 读取excel文件中的第一个工作表
  141. $allColumn = $currentSheet->getHighestColumn(); // 取得最大的列号
  142. $allColumn = Coordinate::columnIndexFromString($allColumn); // 由列名转为列数('AB'->28)
  143. $allRow = $currentSheet->getHighestRow(); // 取得一共有多少行
  144. $arr = [];
  145. for ($currentRow = $startRow; $currentRow <= $allRow; $currentRow++) {
  146. // 从第1列开始输出
  147. for ($currentColumn = 1; $currentColumn <= $allColumn; $currentColumn++) {
  148. $val = $currentSheet->getCellByColumnAndRow($currentColumn, $currentRow)->getValue();
  149. $arr[$currentRow][] = trim($val);
  150. }
  151. // $arr[$currentRow] = array_filter($arr[$currentRow]);
  152. // 统计连续空行
  153. if (empty($arr[$currentRow]) && $emptyRowNum <= 50) {
  154. $emptyRowNum++;
  155. } else {
  156. $emptyRowNum = 0;
  157. }
  158. // 防止坑队友的同事在excel里面弄出很多的空行,陷入很漫长的循环中,设置如果连续超过50个空行就退出循环,返回结果
  159. // 连续50行数据为空,不再读取后面行的数据,防止读满内存
  160. if ($emptyRowNum > 50) {
  161. break;
  162. }
  163. }
  164. $excleDatas[$i] = $arr; // 多个sheet的数组的集合
  165. }
  166. // 这里我只需要用到第一个sheet的数据,所以只返回了第一个sheet的数据
  167. $returnData = $excleDatas ? array_shift($excleDatas) : [];
  168. // 第一行数据就是空的,为了保留其原始数据,第一行数据就不做array_fiter操作;
  169. $returnData = $returnData && isset($returnData[$startRow]) && !empty($returnData[$startRow]) ? array_filter($returnData) : $returnData;
  170. return $returnData;
  171. }
  172. /**
  173. * 格式化内容
  174. *
  175. * @param array $array 头部规则
  176. * @return false|mixed|null|string 内容值
  177. */
  178. protected static function formatting(array $array, $value, $row)
  179. {
  180. !isset($array[2]) && $array[2] = 'text';
  181. switch ($array[2]) {
  182. // 文本
  183. case 'text' :
  184. return $value;
  185. break;
  186. // 日期
  187. case 'date' :
  188. return !empty($value) ? date($array[3], $value) : null;
  189. break;
  190. // 选择框
  191. case 'selectd' :
  192. return $array[3][$value] ?? null;
  193. break;
  194. // 匿名函数
  195. case 'function' :
  196. return isset($array[3]) ? call_user_func($array[3], $row) : null;
  197. break;
  198. // 默认
  199. default :
  200. break;
  201. }
  202. return null;
  203. }
  204. /**
  205. * 解析字段
  206. *
  207. * @param $row
  208. * @param $field
  209. * @return mixed
  210. */
  211. protected static function formattingField($row, $field)
  212. {
  213. $newField = explode('.', $field);
  214. if (count($newField) == 1) {
  215. return $row[$field];
  216. }
  217. foreach ($newField as $item) {
  218. if (isset($row[$item])) {
  219. $row = $row[$item];
  220. } else {
  221. break;
  222. }
  223. }
  224. return is_array($row) ? false : $row;
  225. }
  226. }