本文目录导读:

我来为您设计一个完整的PHP导入导出工具封装,支持Excel、CSV格式,并提供灵活的配置选项。
基础导出导入类
<?php
/**
* 数据导出导入工具类
* 支持Excel(需要PHPExcel/PHPOffice)、CSV格式
*/
class DataExportImport {
private $config;
private $errors = [];
/**
* 构造函数
* @param array $config 配置选项
*/
public function __construct($config = []) {
$this->config = array_merge([
'export_path' => __DIR__ . '/exports/',
'import_path' => __DIR__ . '/imports/',
'max_file_size' => 10485760, // 10MB
'allowed_extensions' => ['xls', 'xlsx', 'csv'],
'chunk_size' => 1000, // 分批处理数量
'time_limit' => 300, // 最大执行时间(秒)
], $config);
// 设置PHP执行时间
if ($this->config['time_limit']) {
set_time_limit($this->config['time_limit']);
}
// 创建目录
$this->createDirectories();
}
/**
* 创建必要的目录
*/
private function createDirectories() {
foreach (['export_path', 'import_path'] as $path) {
if (!is_dir($this->config[$path])) {
mkdir($this->config[$path], 0755, true);
}
}
}
/**
* 导出数据到Excel
* @param array $headers 表头
* @param array $data 数据
* @param string $filename 文件名
* @return string|false 文件路径或false
*/
public function exportToExcel($headers, $data, $filename = '') {
try {
// 检查PHPExcel类是否存在
if (!class_exists('PHPExcel') && !class_exists('PhpOffice\PhpSpreadsheet\Spreadsheet')) {
throw new Exception('请安装 PHPExcel 或 PhpOffice\PhpSpreadsheet 库');
}
$filename = $filename ?: date('YmdHis') . '_export';
$filename = $this->checkFilename($filename);
if (class_exists('PhpOffice\PhpSpreadsheet\Spreadsheet')) {
// 使用 PhpSpreadsheet
$spreadsheet = new \PhpOffice\PhpSpreadsheet\Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
// 写入表头
foreach ($headers as $key => $value) {
$sheet->setCellValueByColumnAndRow($key + 1, 1, $value);
}
// 写入数据
$rowNum = 2;
foreach ($data as $row) {
foreach ($row as $colNum => $value) {
$sheet->setCellValueByColumnAndRow($colNum + 1, $rowNum, $value);
}
$rowNum++;
}
// 保存文件
$writer = new \PhpOffice\PhpSpreadsheet\Writer\Xlsx($spreadsheet);
$filePath = $this->config['export_path'] . $filename . '.xlsx';
$writer->save($filePath);
// 自动下载
if (php_sapi_name() !== 'cli') {
$this->downloadFile($filePath, true);
}
return $filePath;
} else {
// 使用 PHPExcel
$this->exportWithPHPExcel($headers, $data, $filename);
}
} catch (Exception $e) {
$this->errors[] = '导出失败: ' . $e->getMessage();
return false;
}
}
/**
* 导出数据到CSV
* @param array $headers 表头
* @param array $data 数据
* @param string $filename 文件名
* @param string $delimiter 分隔符
* @return string|false 文件路径或false
*/
public function exportToCSV($headers, $data, $filename = '', $delimiter = ',') {
try {
$filename = $filename ?: date('YmdHis') . '_export';
$filename = $this->checkFilename($filename);
$filePath = $this->config['export_path'] . $filename . '.csv';
// 打开文件流
$file = fopen($filePath, 'w');
// 添加BOM,防止中文乱码
fwrite($file, "\xEF\xBB\xBF");
// 写入表头
if (!empty($headers)) {
fputcsv($file, $headers, $delimiter);
}
// 写入数据
foreach ($data as $row) {
fputcsv($file, $row, $delimiter);
}
fclose($file);
// 自动下载
if (php_sapi_name() !== 'cli') {
$this->downloadFile($filePath, true);
}
return $filePath;
} catch (Exception $e) {
$this->errors[] = '导出失败: ' . $e->getMessage();
return false;
}
}
/**
* 使用PHPExcel导出
* @param array $headers
* @param array $data
* @param string $filename
*/
private function exportWithPHPExcel($headers, $data, $filename) {
$objPHPExcel = new PHPExcel();
$sheet = $objPHPExcel->getActiveSheet();
// 设置表头
foreach ($headers as $key => $value) {
$sheet->setCellValueByColumnAndRow($key, 1, $value);
}
// 写入数据
$rowNum = 2;
foreach ($data as $row) {
foreach ($row as $colNum => $value) {
$sheet->setCellValueByColumnAndRow($colNum, $rowNum, $value);
}
$rowNum++;
}
// 创建Excel5写入器
$objWriter = PHPExcel_IOFactory::createWriter($objPHPExcel, 'Excel5');
$filePath = $this->config['export_path'] . $filename . '.xls';
$objWriter->save($filePath);
if (php_sapi_name() !== 'cli') {
$this->downloadFile($filePath, true);
}
return $filePath;
}
/**
* 导入Excel文件
* @param string $file 文件路径
* @param array $mapping 字段映射
* @return array|false 数据数组或false
*/
public function importFromExcel($file, $mapping = []) {
try {
// 文件校验
$this->validateFile($file);
$spreadsheet = null;
if (class_exists('PhpOffice\PhpSpreadsheet\Spreadsheet')) {
$reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReaderForFile($file);
$spreadsheet = $reader->load($file);
$rows = $spreadsheet->getActiveSheet()->toArray();
} else {
// 使用PHPExcel
$objPHPExcel = PHPExcel_IOFactory::load($file);
$rows = $objPHPExcel->getActiveSheet()->toArray();
}
if (empty($rows)) {
return [];
}
// 获取表头
$headers = array_shift($rows);
// 处理数据
$data = [];
foreach ($rows as $rowIndex => $row) {
$item = [];
foreach ($headers as $colIndex => $header) {
$key = $header;
// 如果有映射,则使用映射后的字段名
if (!empty($mapping) && isset($mapping[$header])) {
$key = $mapping[$header];
}
$item[$key] = isset($row[$colIndex]) ? $row[$colIndex] : '';
}
$data[] = $item;
}
return $data;
} catch (Exception $e) {
$this->errors[] = '导入失败: ' . $e->getMessage();
return false;
}
}
/**
* 导入CSV文件
* @param string $file 文件路径
* @param array $mapping 字段映射
* @param string $delimiter 分隔符
* @return array|false 数据数组或false
*/
public function importFromCSV($file, $mapping = [], $delimiter = ',') {
try {
// 文件校验
$this->validateFile($file, ['csv']);
$data = [];
$handle = fopen($file, 'r');
// 读取BOM
$bom = fread($handle, 3);
if ($bom != "\xEF\xBB\xBF") {
rewind($handle);
}
// 获取表头
$headers = fgetcsv($handle, 0, $delimiter);
// 处理数据
while (($row = fgetcsv($handle, 0, $delimiter)) !== false) {
$item = [];
foreach ($headers as $colIndex => $header) {
$key = $header;
// 如果有映射,则使用映射后的字段名
if (!empty($mapping) && isset($mapping[$header])) {
$key = $mapping[$header];
}
$item[$key] = isset($row[$colIndex]) ? $row[$colIndex] : '';
}
$data[] = $item;
}
fclose($handle);
return $data;
} catch (Exception $e) {
$this->errors[] = '导入失败: ' . $e->getMessage();
return false;
}
}
/**
* 上传文件
* @param array $file $_FILES中的文件数组
* @return string|false 上传后的文件路径或false
*/
public function uploadFile($file) {
try {
// 检查上传文件
if (!isset($file['error']) || is_array($file['error'])) {
throw new Exception('上传文件错误');
}
// 检查错误码
switch ($file['error']) {
case UPLOAD_ERR_OK:
break;
case UPLOAD_ERR_NO_FILE:
throw new Exception('没有文件被上传');
case UPLOAD_ERR_INI_SIZE:
case UPLOAD_ERR_FORM_SIZE:
throw new Exception('文件大小超出限制');
default:
throw new Exception('未知错误');
}
// 检查文件大小
if ($file['size'] > $this->config['max_file_size']) {
throw new Exception('文件大小超出限制');
}
// 生成唯一文件名
$extension = pathinfo($file['name'], PATHINFO_EXTENSION);
$filename = date('YmdHis') . '_' . uniqid() . '.' . $extension;
$targetPath = $this->config['import_path'] . $filename;
// 移动文件
if (move_uploaded_file($file['tmp_name'], $targetPath)) {
return $targetPath;
} else {
throw new Exception('文件上传失败');
}
} catch (Exception $e) {
$this->errors[] = '上传失败: ' . $e->getMessage();
return false;
}
}
/**
* 校验文件
* @param string $file 文件路径
* @param array $allowedExtensions 允许的扩展名
*/
public function validateFile($file, $allowedExtensions = null) {
if (!file_exists($file)) {
throw new Exception('文件不存在');
}
if (!is_readable($file)) {
throw new Exception('文件不可读');
}
$allowedExtensions = $allowedExtensions ?: $this->config['allowed_extensions'];
$extension = strtolower(pathinfo($file, PATHINFO_EXTENSION));
if (!in_array($extension, $allowedExtensions)) {
throw new Exception('文件类型不允许');
}
return true;
}
/**
* 分批导出大文件
* @param callable $callback 数据获取回调函数
* @param callable $processCallback 处理回调(可选)
* @param string $filename 文件名
* @return string|false
*/
public function exportLargeData($callback, $processCallback = null, $filename = '') {
try {
$filename = $filename ?: date('YmdHis') . '_large_export';
$filePath = $this->config['export_path'] . $filename . '.csv';
$file = fopen($filePath, 'w');
fwrite($file, "\xEF\xBB\xBF"); // 添加BOM
$page = 1;
while (true) {
// 获取数据
$data = call_user_func($callback, $page, $this->config['chunk_size']);
if (empty($data)) {
break;
}
// 是否有处理函数
if ($processCallback) {
$data = call_user_func($processCallback, $data);
}
// 写入数据
foreach ($data as $row) {
fputcsv($file, $row);
}
$page++;
}
fclose($file);
return $filePath;
} catch (Exception $e) {
$this->errors[] = '导出失败: ' . $e->getMessage();
return false;
}
}
/**
* 分批导入大文件
* @param string $file 文件路径
* @param callable $processCallback 处理每行数据的回调函数
* @return int|false 处理的行数或false
*/
public function importLargeData($file, $processCallback) {
try {
// 文件校验
$this->validateFile($file, ['csv']);
$handle = fopen($file, 'r');
$rowCount = 0;
// 读取BOM
$bom = fread($handle, 3);
if ($bom != "\xEF\xBB\xBF") {
rewind($handle);
}
// 跳过表头
fgetcsv($handle, 0, ',');
// 逐行处理
while (($row = fgetcsv($handle, 0, ',')) !== false) {
if (!empty($row)) {
// 调用处理函数
$result = call_user_func($processCallback, $row);
if ($result === false) {
break;
}
$rowCount++;
}
}
fclose($handle);
return $rowCount;
} catch (Exception $e) {
$this->errors[] = '导入失败: ' . $e->getMessage();
return false;
}
}
/**
* 下载文件
* @param string $filePath 文件路径
* @param boolean $deleteAfter 下载后是否删除
*/
public function downloadFile($filePath, $deleteAfter = false) {
if (!file_exists($filePath)) {
throw new Exception('文件不存在');
}
$filename = basename($filePath);
// 设置HTTP头
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="' . $filename . '"');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize($filePath));
// 输出文件内容
readfile($filePath);
if ($deleteAfter) {
@unlink($filePath);
}
exit;
}
/**
* 清理文件名
* @param string $filename
* @return string
*/
private function checkFilename($filename) {
// 移除非法字符
$filename = preg_replace('/[^a-zA-Z0-9_\-\.]/', '_', $filename);
// 防止路径穿越
$filename = str_replace(['../', './', '..\\', '.\\'], '', $filename);
return $filename;
}
/**
* 获取错误信息
* @return array
*/
public function getErrors() {
return $this->errors;
}
/**
* 清除错误信息
*/
public function clearErrors() {
$this->errors = [];
}
/**
* 检查是否有错误
* @return boolean
*/
public function hasErrors() {
return !empty($this->errors);
}
}
?>
服务类封装(业务逻辑层)
<?php
/**
* 导入导出服务类
* 提供业务级别的导入导出功能
*/
class DataTransferService {
private $dte;
private $withHeaders = 'withHeaders'; // 隐私保护示例
/**
* 构造函数
*/
public function __construct($config = []) {
$this->dte = new DataExportImport($config);
}
/**
* 通用导出方法
* @param array $params 导出参数
* @return array [status, message, data]
*/
public function export($params) {
try {
$type = isset($params['type']) ? strtolower($params['type']) : 'csv';
$headers = isset($params['headers']) ? $params['headers'] : [];
$data = isset($params['data']) ? $params['data'] : [];
$filename = isset($params['filename']) ? $params['filename'] : '';
// 验证数据
if (empty($data)) {
return ['status' => false, 'message' => '没有数据可导出', 'data' => null];
}
// 根据类型导出
if ($type == 'excel') {
$result = $this->dte->exportToExcel($headers, $data, $filename);
} else {
$result = $this->dte->exportToCSV($headers, $data, $filename);
}
if ($result !== false) {
return ['status' => true, 'message' => '导出成功', 'data' => $result];
}
return ['status' => false, 'message' => $this->getErrorMessage(), 'data' => null];
} catch (Exception $e) {
return ['status' => false, 'message' => $e->getMessage(), 'data' => null];
}
}
/**
* 生成Excel模板
* @param array $headers 表头
* @param string $filename 文件名
* @return array
*/
public function exportTemplate($headers, $filename = '导入模板') {
try {
// 添加示例数据
$exampleData = [];
if ($this->isAssocArray($headers)) {
// 如果是关联数组,只有键作为表头
$headers = array_keys($headers);
}
// 生成示例数据(空一行)
foreach ($headers as $header) {
$exampleData[] = ['']; // 空示例行
}
// 创建模板
$result = $this->dte->exportToExcel($headers, $exampleData, $filename);
if ($result !== false) {
return ['status' => true, 'message' => '模板生成成功', 'data' => $result];
}
return ['status' => false, 'message' => $this->getErrorMessage(), 'data' => null];
} catch (Exception $e) {
return ['status' => false, 'message' => $e->getMessage(), 'data' => null];
}
}
/**
* 通用导入方法
* @param array $params 导入参数
* @return array [status, message, data]
*/
public function import($params) {
try {
// 上传文件
if (isset($params['file'])) {
$filePath = $this->dte->uploadFile($params['file']);
if ($filePath === false) {
return ['status' => false, 'message' => $this->getErrorMessage(), 'data' => null];
}
} elseif (isset($params['path'])) {
$filePath = $params['path'];
} else {
return ['status' => false, 'message' => '没有找到文件', 'data' => null];
}
// 获取文件类型
$type = isset($params['type']) ? strtolower($params['type']) :
strtolower(pathinfo($filePath, PATHINFO_EXTENSION));
// 字段映射
$mapping = isset($params['mapping']) ? $params['mapping'] : [];
// 根据类型导入
if ($type == 'csv') {
$data = $this->dte->importFromCSV($filePath, $mapping);
} else {
$data = $this->dte->importFromExcel($filePath, $mapping);
}
if ($data !== false) {
return ['status' => true, 'message' => '导入成功', 'data' => $data];
}
return ['status' => false, 'message' => $this->getErrorMessage(), 'data' => null];
} catch (Exception $e) {
return ['status' => false, 'message' => $e->getMessage(), 'data' => null];
}
}
/**
* 导入并处理(适用于文件导入数据库)
* @param string $filePath 文件路径
* @param string $type 文件类型
* @param callable $processor 处理函数
* @return int|false 处理成功条数
*/
public function importAndProcess($filePath, $type, $processor) {
try {
// 使用分批导入处理大文件
$result = $this->dte->importLargeData($filePath, function($row) use ($processor) {
// 调用处理函数
return call_user_func_array($processor, [$row]);
});
return $result;
} catch (Exception $e) {
return false;
}
}
/**
* 检查数组是否为关联数组
* @param array $arr
* @return boolean
*/
private function isAssocArray(array $arr) {
if (empty($arr)) {
return false;
}
return array_keys($arr) !== range(0, count($arr) - 1);
}
/**
* 获取错误信息
* @return string
*/
private function getErrorMessage() {
$errors = $this->dte->getErrors();
return $errors ? implode('; ', $errors) : '操作失败';
}
}
?>
实用示例
<?php
/**
* 使用示例
*/
// 配置
$config = [
'export_path' => dirname(__FILE__) . '/exports/',
'import_path' => dirname(__FILE__) . '/imports/',
'max_file_size' => 20971520, // 20MB
'chunk_size' => 500,
];
// 1. 基础使用 - 导出
$dte = new DataExportImport($config);
$headers = ['ID', '姓名', '邮箱', '电话'];
$data = [
[1, '张三', 'zhangsan@example.com', '13800000001'],
[2, '李四', 'lisi@example.com', '13800000002'],
[3, '王五', 'wangwu@example.com', '13800000003'],
];
// 导出Excel
$result = $dte->exportToExcel($headers, $data, '用户列表');
// 导出CSV
$result = $dte->exportToCSV($headers, $data, '用户列表');
// 2. 使用服务类 - 更高级的封装
$service = new DataTransferService($config);
$params = [
'type' => 'excel',
'headers' => ['ID', '名称', '价格'],
'data' => [[1, '商品A', 99.9], [2, '商品B', 199.9]],
'filename' => '商品列表'
];
$result = $service->export($params);
// 3. 导入示例
$file = [
'error' => UPLOAD_ERR_OK,
'size' => 1024 * 1024,
'tmp_name' => '/tmp/phpXXXX',
'name' => 'test.xlsx'
];
$params = [
'file' => $file,
'type' => 'xlsx',
'mapping' => [
'姓名' => 'name',
'邮箱' => 'email',
'电话' => 'phone'
]
];
$result = $service->import($params);
// 4. 大批量数据处理
$dte->exportLargeData(
function($page, $chunkSize) {
// 从数据库获取数据
$data = getDataFromDB($page, $chunkSize);
return $data;
},
null,
'大量数据导出'
);
// 5. 异常处理
if ($dte->hasErrors()) {
echo "错误信息: ";
print_r($dte->getErrors());
}
?>
Composer依赖
{
"require": {
"phpoffice/phpspreadsheet": "^1.24",
"ext-json": "*",
"ext-fileinfo": "*"
}
}
配置文件示例
<?php
return [
'export_path' => env('EXPORT_PATH', storage_path('exports')),
'import_path' => env('IMPORT_PATH', storage_path('imports')),
'max_file_size' => env('MAX_FILE_SIZE', 10485760),
'allowed_extensions' => ['xls', 'xlsx', 'csv'],
'chunk_size' => 1000,
'time_limit' => 300,
'csv_delimiter' => ',',
'csv_encoding' => 'utf-8',
];
?>
这套封装提供了:
- 灵活的导出功能 - 支持Excel和CSV格式
- 可靠的导入功能 - 支持文件上传、格式校验
- 大批量数据处理 - 分批处理防止内存溢出
- 完善的错误处理 - 明确区分错误类型
- 配置灵活 - 可自定义路径、大小限制等
使用时需要安装相应依赖库,推荐使用更好的PhpSpreadsheet库,这个封装类可以根据具体业务需求进行扩展和优化。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播