找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 9|回复: 0

[技巧分享] 完整的ZIP文件处理解决方案(兼容PHP 8.3)

[复制链接]
  • TA的每日心情
    奋斗
    前天 05:21
  • 签到天数: 5 天

    连续签到: 1 天

    [LV.2]偶尔看看I

    766680204
    发表于 前天 05:27 | 显示全部楼层 |阅读模式

    您需要 登录 才可以下载或查看,没有账号?立即注册

    ×
    /**
    * 完整的ZIP文件处理解决方案(兼容PHP 8.3)
    * 功能:下载ZIP文件、验证MD5、解压加密ZIP
    */
    1. class ZipProcessor {
    2.     private $tempDir;

    3.     public function __construct() {
    4.         $this->tempDir = sys_get_temp_dir();
    5.         $this->checkDependencies();
    6.     }
    7.     private function checkDependencies() {
    8.         if (!extension_loaded('curl')) {
    9.             throw new Exception("需要CURL扩展支持");
    10.         }

    11.         if (!extension_loaded('zip')) {
    12.             throw new Exception("需要ZIP扩展支持");
    13.         }
    14.     }

    15.     public function processApiResponse($apiResponse, $extractDir) {
    16.         try {
    17.            
    18.             $responseData = $this->parseApiResponse($apiResponse);

    19.             
    20.             $zipFile = $this->downloadZipFile($responseData['download_url']);

    21.            
    22.             $this->verifyFileIntegrity($zipFile, $responseData['zip_md5'], $responseData['zip_size']);

    23.             
    24.             $this->extractZipFile($zipFile, $extractDir, $responseData['zip_password']);

    25.             
    26.             $this->cleanupTempFiles($zipFile);

    27.             return [
    28.                 'success' => true,
    29.                 'message' => 'ZIP文件处理成功',
    30.                 'version' => $responseData['version'],
    31.                 'extract_dir' => realpath($extractDir)
    32.             ];

    33.         } catch (Exception $e) {
    34.             if (isset($zipFile) && file_exists($zipFile)) {
    35.                 $this->cleanupTempFiles($zipFile);
    36.             }

    37.             return [
    38.                 'success' => false,
    39.                 'message' => $e->getMessage()
    40.             ];
    41.         }
    42.     }

    43.     /**
    44.      * 解析API响应
    45.      */
    46.     private function parseApiResponse($apiResponse) {
    47.         $responseData = json_decode($apiResponse, true);

    48.         if (json_last_error() !== JSON_ERROR_NONE) {
    49.             throw new Exception("JSON解析失败: " . json_last_error_msg());
    50.         }

    51.         if (!isset($responseData['code']) || $responseData['code'] !== 0) {
    52.             throw new Exception("API错误: " . ($responseData['msg'] ?? '未知错误'));
    53.         }

    54.         if (!isset($responseData['data'])) {
    55.             throw new Exception("API响应格式错误: 缺少data字段");
    56.         }

    57.         $requiredFields = ['download_url', 'zip_password', 'zip_md5', 'version'];
    58.         foreach ($requiredFields as $field) {
    59.             if (!isset($responseData['data'][$field])) {
    60.                 throw new Exception("API响应格式错误: 缺少data字段 $field");
    61.             }
    62.         }

    63.         return $responseData['data'];
    64.     }

    65.     /**
    66.      * 下载ZIP文件
    67.      */
    68.     private function downloadZipFile($url) {
    69.         $tempFile = tempnam($this->tempDir, 'zip_download_');

    70.         $ch = curl_init($url);
    71.         $fp = fopen($tempFile, 'w');

    72.         if (!$fp) {
    73.             throw new Exception("无法创建临时文件: $tempFile");
    74.         }

    75.         curl_setopt_array($ch, [
    76.             CURLOPT_FILE => $fp,
    77.             CURLOPT_FOLLOWLOCATION => true,
    78.             CURLOPT_AUTOREFERER => true,
    79.             CURLOPT_MAXREDIRS => 5,
    80.             CURLOPT_TIMEOUT => 60,
    81.             CURLOPT_CONNECTTIMEOUT => 30,
    82.             CURLOPT_SSL_VERIFYPEER => false,
    83.             CURLOPT_SSL_VERIFYHOST => false,
    84.             CURLOPT_USERAGENT => 'PHP-ZipProcessor/1.0'
    85.         ]);

    86.         $result = curl_exec($ch);

    87.         if ($result === false) {
    88.             $error = curl_error($ch);
    89.             fclose($fp);
    90.             unlink($tempFile);
    91.             curl_close($ch);
    92.             throw new Exception("下载失败: $error");
    93.         }

    94.         $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    95.         $contentType = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);

    96.         curl_close($ch);
    97.         fclose($fp);

    98.         if ($httpCode < 200 || $httpCode >= 300) {
    99.             unlink($tempFile);
    100.             throw new Exception("HTTP错误: $httpCode");
    101.         }

    102.         if (!in_array($contentType, ['application/zip', 'application/x-zip-compressed', 'application/octet-stream'])) {
    103.             unlink($tempFile);
    104.             throw new Exception("下载的文件不是ZIP格式: $contentType");
    105.         }

    106.         if (filesize($tempFile) === 0) {
    107.             unlink($tempFile);
    108.             throw new Exception("下载的文件为空");
    109.         }

    110.         return $tempFile;
    111.     }

    112.     /**
    113.      * 验证文件完整性
    114.      */
    115.     private function verifyFileIntegrity($filePath, $expectedMd5, $expectedSize = null) {
    116.         // 验证MD5
    117.         $actualMd5 = md5_file($filePath);
    118.         if (strtolower($actualMd5) !== strtolower($expectedMd5)) {
    119.             throw new Exception("MD5验证失败\n实际: $actualMd5\n期望: $expectedMd5");
    120.         }

    121.         // 验证文件大小(如果提供)
    122.         if ($expectedSize !== null && $expectedSize !== '0') {
    123.             $actualSize = filesize($filePath);
    124.             $expectedBytes = $this->humanToBytes($expectedSize);

    125.             if ($expectedBytes > 0 && abs($actualSize - $expectedBytes) > 1024) { // 允许1KB误差
    126.                 throw new Exception("文件大小不匹配\n实际: " . $this->bytesToHuman($actualSize) .
    127.                                   "\n期望: $expectedSize");
    128.             }
    129.         }
    130.     }

    131.     /**
    132.      * 解压ZIP文件(兼容PHP 8.3)
    133.      */
    134.     private function extractZipFile($zipFile, $extractDir, $password) {
    135.         // 创建解压目录
    136.         if (!file_exists($extractDir)) {
    137.             if (!mkdir($extractDir, 0755, true)) {
    138.                 throw new Exception("无法创建解压目录: $extractDir");
    139.             }
    140.         }

    141.         if (!is_writable($extractDir)) {
    142.             throw new Exception("解压目录不可写: $extractDir");
    143.         }

    144.         $zip = new ZipArchive();
    145.         $openResult = $zip->open($zipFile);

    146.         if ($openResult !== true) {
    147.             throw new Exception("无法打开ZIP文件: " . $this->getZipErrorText($openResult));
    148.         }

    149.         try {
    150.             // 尝试使用密码(兼容PHP 8.3)
    151.             $zip->setPassword($password);

    152.             // 检查ZIP文件是否有内容
    153.             if ($zip->numFiles === 0) {
    154.                 throw new Exception("ZIP文件为空");
    155.             }

    156.             // 尝试解压所有文件
    157.             $success = false;

    158.             // 先尝试解压第一个文件测试密码
    159.             $firstFileName = $zip->getNameIndex(0);
    160.             if ($zip->extractTo($extractDir, $firstFileName)) {
    161.                 // 密码正确,继续解压其他文件
    162.                 for ($i = 1; $i < $zip->numFiles; $i++) {
    163.                     $fileName = $zip->getNameIndex($i);
    164.                     $zip->extractTo($extractDir, $fileName);
    165.                 }
    166.                 $success = true;
    167.             } else {
    168.                 // 可能是密码错误,尝试不使用密码
    169.                 $zip->setPassword('');
    170.                 if ($zip->extractTo($extractDir)) {
    171.                     $success = true;
    172.                 } else {
    173.                     // 检查具体错误
    174.                     if ($openResult === ZipArchive::ER_WRONGPASSWD) {
    175.                         throw new Exception("ZIP文件密码错误");
    176.                     }
    177.                     throw new Exception("解压失败");
    178.                 }
    179.             }

    180.             if (!$success) {
    181.                 throw new Exception("解压失败");
    182.             }

    183.         } finally {
    184.             $zip->close();
    185.         }

    186.         return true;
    187.     }

    188.     private function cleanupTempFiles($filePath) {
    189.         if (file_exists($filePath)) {
    190.             unlink($filePath);
    191.         }
    192.     }

    193.     /**
    194.      * 获取ZIP错误信息
    195.      */
    196.     private function getZipErrorText($errorCode) {
    197.         $errors = [
    198.             ZipArchive::ER_EXISTS => '文件已存在',
    199.             ZipArchive::ER_INCONS => 'Zip文件不一致',
    200.             ZipArchive::ER_INVAL => '无效的参数',
    201.             ZipArchive::ER_MEMORY => '内存分配失败',
    202.             ZipArchive::ER_NOENT => '没有这样的文件',
    203.             ZipArchive::ER_NOZIP => '不是zip归档',
    204.             ZipArchive::ER_OPEN => '无法打开文件',
    205.             ZipArchive::ER_READ => '读取错误',
    206.             ZipArchive::ER_SEEK => '查找错误',
    207.             ZipArchive::ER_WRONGPASSWD => '密码错误'
    208.         ];

    209.         return $errors[$errorCode] ?? "未知错误: $errorCode";
    210.     }

    211.     /**
    212.      * 人类可读大小转字节
    213.      */
    214.     private function humanToBytes($size) {
    215.         if ($size === null || $size === '') return 0;

    216.         $units = ['B', 'KB', 'MB', 'GB', 'TB'];
    217.         $unit = strtoupper(substr($size, -2));

    218.         if (!in_array($unit, $units)) {
    219.             $unit = strtoupper(substr($size, -1));
    220.         }

    221.         $value = (float)trim(rtrim($size, $unit));

    222.         $power = array_search($unit, $units);
    223.         return $value * pow(1024, $power);
    224.     }

    225.     /**
    226.      * 字节转人类可读大小
    227.      */
    228.     private function bytesToHuman($bytes) {
    229.         $units = ['B', 'KB', 'MB', 'GB', 'TB'];
    230.         $power = floor(log($bytes, 1024));
    231.         return round($bytes / pow(1024, $power), 2) . $units[$power];
    232.     }
    233. }

    234. // 使用示例
    235. try {
    236.     // API响应数据
    237.     $apiResponse = '{
    238.         "code": 0,
    239.         "msg": "加密更新包生成成功",
    240.         "data": {
    241.             "download_url": ",
    242.             "zip_password": "e26d556899c456915a61341802dad137",
    243.             "zip_size": "3.56KB",
    244.             "zip_md5": "90e356e66c38a0c5c9d023e94aa750e1",
    245.             "version": "3.1.20250928",
    246.             "cxid": 2
    247.         }
    248.     }';
    249.     $extractDirectory = ROOT;
    250.     $processor = new ZipProcessor();

    251.     // 执行处理流程
    252.     $result = $processor->processApiResponse($apiResponse, $extractDirectory);

    253.     if ($result['success']) {
    254.         echo "✅ 处理成功!\n";
    255.         echo "📁 解压目录: " . $result['extract_dir'] . "\n";
    256.         echo "🔢 版本号: " . $result['version'] . "\n";
    257.     } else {
    258.         echo "❌ 处理失败: " . $result['message'] . "\n";
    259.     }

    260. } catch (Exception $e) {
    261.     echo "⚠️  系统错误: " . $e->getMessage() . "\n";
    262. }
    复制代码



    回复

    使用道具 举报

    网站地图|页面地图|Archiver|手机版|小黑屋|找资源 |网站地图

    GMT+8, 2025-11-30 23:55

    Powered by Discuz! X3.5

    © 2001-2025 Discuz! Team.

    快速回复 返回顶部 返回列表