Files
orico-official-website/app/admin/common.php
jsasg 9c81113fa7
All checks were successful
Gitea Actions Official-website / deploy-dev (push) Successful in 4s
refactor: 产品兼容性导出
2026-08-12 16:50:18 +08:00

266 lines
9.6 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
// 这是系统自动生成的公共文件
if (!function_exists('xlsx_reader')) {
/**
* 读取Excel数据
* @param string $file Excel文件路径
* @param int $initial_row 初始行
* @param array $keys_map 列 -> 键 例:['A' => 'name', 'B' => 'age', 'C' => 'sex']
* @return array
*/
function xlsx_reader(string $file, int $initial_row = 1, array $keys_map = [], bool $read_only = false): array
{
$reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReader('Xlsx');
if ($read_only) {
// 只读模式,不读取公式,样式等等,减少内存消耗
$reader->setReadDataOnly(true);
}
// 读取文件
$spreadsheet = $reader->load($file);
// 获取活动sheet
$sheet = $spreadsheet->getActiveSheet();
// 获取最大行
$max_row = $sheet->getHighestRow();
// 获取最大列
$max_col = $sheet->getHighestColumn();
$xlsx_data = [];
$first_col = ord('A');
$last_col = ord($max_col);
for ($row = $initial_row; $row <= $max_row; $row++) {
for ($col = $first_col; $col <= $last_col; $col++) {
$key = $col_chr = chr($col);
if (!empty($keys_map)) {
$key = $keys_map[$col_chr] ?? $col_chr;
}
$xlsx_data[$row][$key] = $sheet->getCell($col_chr . $row)->getValue();
}
}
return $xlsx_data;
}
}
if (!function_exists('xlsx_stream_reader')) {
/**
* 读取Excel数据
* @param string $file Excel文件路径
* @param int $initial_row 初始行
* @param array $keys_map 列 -> 键 例:['A' => 'name', 'B' => 'age', 'C' => 'sex']
* @return Generator
*/
function xlsx_stream_reader(string $file, int $initial_row = 1, array $keys_map = [], bool $read_only = false): Generator
{
$reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReader('Xlsx');
if ($read_only) {
// 只读模式,不读取公式,样式等等,减少内存消耗
$reader->setReadDataOnly(true);
}
// 读取文件
$spreadsheet = $reader->load($file);
// 获取活动sheet
$sheet = $spreadsheet->getActiveSheet();
// 获取最大行
$max_row = $sheet->getHighestRow();
// 获取最大列
$max_col = $sheet->getHighestColumn();
$xlsx_row = [];
$first_col = ord('A');
$last_col = ord($max_col);
for ($row = $initial_row; $row <= $max_row; $row++) {
for ($col = $first_col; $col <= $last_col; $col++) {
$key = $col_chr = chr($col);
if (!empty($keys_map)) {
$key = $keys_map[$col_chr] ?? $col_chr;
}
$xlsx_row[$key] = $sheet->getCell($col_chr . $row)->getValue();
}
yield $xlsx_row;
}
// 主动销毁对象以释放内存
$spreadsheet->disconnectWorksheets();
unset($spreadsheet);
}
}
if (!function_exists('xlsx_writer')) {
/**
* 构建Excel数据
* @param array $data 数据 例:[['name'=>'张三','age'=>18, 'sex'=>'男']]
* @param array $schema 表头信息 例:['name' => '姓名', 'age' => '年龄', 'sex' => '性别']
* @param string $filename 下载文件名称 默认为YmdHis时间
* @param string $output 输出方式 php://output 或 文件路径
* @return \think\Response
*/
function xlsx_writer(array|\think\model\Collection|Generator $data, array $schema, string $filename = '', string $output = 'php://output'): \think\Response
{
// 获取Spreadsheet对象
$spreadsheet = new \PhpOffice\PhpSpreadsheet\Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
// 写入表头
$title = array_values($schema);
$title_col = 'A';
foreach ($title as $value) {
// 单元格内容写入
$sheet->setCellValue($title_col . '1', $value);
$title_col++;
}
// 写入数据
$row = 2;
$keys = array_keys($schema);
foreach ($data as $item) {
$data_col = 'A';
foreach ($keys as $key) {
$sheet->setCellValue($data_col . $row, $item[$key] ?? '');
$data_col++;
}
$row++;
}
flush();
ob_flush();
if ($filename == '') $filename = date('YmdHis');
$filename = urlencode($filename);
// 文件写入缓冲区
\PhpOffice\PhpSpreadsheet\IOFactory::createWriter($spreadsheet, 'Xlsx')->save($output);
// 从缓冲区获取导出文件并通过Response返回解决thinkphp框架的Response无法获取设置的header问题
$file_data = ob_get_clean();
$response = \think\Response::create($file_data)->header([
'Access-Control-Expose-Headers' => 'Content-Disposition',
'Content-Type' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet; charset=UTF-8',
'Content-Disposition' => 'attachment;filename=' . $filename . '.xlsx',
'Cache-Control' => 'max-age=0'
]);
return $response;
}
}
if (!function_exists('xlsx_download_xaccel')) {
/**
* 构建Excel数据采用Nginx Sendfile方式
*
* Nginx中需配置
* location /download/ {
* internal; # 关键:禁止外部直接访问这个路径
* alias /tmp/; # 使用 alias将 URI 映射到你存放文件的真实目录
* }
*
* 注意:避免临时文件造成磁盘堆积,需依赖系统层定时清理(如 systemd-tmpfiles / cronPHP 侧不做删除
* 如:# 每分钟执行一次,删除 /tmp 下 10 分钟前创建的 xlsx_ 临时文件
* * * * * * find /tmp -maxdepth 1 -name 'ow_xlsx_*' -type f -mmin +10 -delete
*
* @param array|\think\model\Collection|Generator $data 数据 例:[['name'=>'张三','age'=>18, 'sex'=>'男']]
* @param array $schema 表头信息 例:['name' => '姓名', 'age' => '年龄', 'sex' => '性别']
* @param string $filename 下载文件名称 默认为YmdHis时间
* @return \think\Response
*/
function xlsx_download_xaccel(array|\think\model\Collection|Generator $data, array $schema, string $filename = ''): \think\Response
{
// 生成到临时文件,避免将整个文件内容写入输出缓冲区
$tmp_file = tempnam(sys_get_temp_dir(), 'ow_xlsx_');
if (false === $tmp_file) {
throw new \RuntimeException('创建临时文件失败');
}
// tempnam 默认 0600需放开读权限否则 Nginx 与 PHP-FPM 用户不同时无法读取
chmod($tmp_file, 0644);
// 获取Spreadsheet对象
$spreadsheet = new \PhpOffice\PhpSpreadsheet\Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
// 写入表头
$title_col = 'A';
foreach (array_values($schema) as $value) {
$sheet->setCellValue($title_col . '1', $value);
$title_col++;
}
// 写入数据
$row = 2;
$keys = array_keys($schema);
foreach ($data as $item) {
$data_col = 'A';
foreach ($keys as $key) {
$sheet->setCellValue($data_col . $row, $item[$key] ?? '');
$data_col++;
}
$row++;
}
// 写入临时文件,不预计算公式以降低耗时与内存
$writer = new \PhpOffice\PhpSpreadsheet\Writer\Xlsx($spreadsheet);
$writer->setPreCalculateFormulas(false);
$writer->save($tmp_file);
// 释放Spreadsheet内存
$spreadsheet->disconnectWorksheets();
unset($spreadsheet, $writer, $sheet);
if ('' == $filename) $filename = date('YmdHis');
$filename = urlencode($filename);
$response = \think\Response::create()
->header([
'Access-Control-Expose-Headers' => 'Content-Disposition',
'Content-Type' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet; charset=UTF-8',
'Content-Disposition' => 'attachment;filename=' . $filename . '.xlsx',
'Cache-Control' => 'max-age=0',
'Content-Length' => filesize($tmp_file),
'X-Accel-Redirect' => '/download/' . basename($tmp_file), // Nginx
]);
return $response;
}
}
if (!function_exists('strtobytes')) {
/**
* 字符串转字节
* @param string $str_size 字符串 例:'1MB' '2GB'
* @return int
*/
function strtobytes(string $str_size): int
{
preg_match('/(\d+)(\w+)/', $str_size, $matches);
if (!isset($matches[1])) {
throw new \Exception('请确认size大小');
}
if (!isset($matches[2])) {
throw new \Exception('请确认单位');
}
$size = intval($matches[1]);
$unit = $matches[2];
switch (strtolower($unit)) {
case 'kb':
$size *= 1024;
break;
case 'mb':
$size *= 1024 * 1024;
break;
case 'gb':
$size *= 1024 * 1024 * 1024;
break;
case 'tb':
$size *= 1024 * 1024 * 1024 * 1024;
default:
throw new \Exception('不支持[' . $unit . ']单位');
}
return $size;
}
}