You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用PHP提取Java RandomAccessFile创建的归档文件?

PHP归档提取逻辑实现与内存溢出解决方案

一、归档文件格式分析(从Java代码逆向)

从提供的Java归档生成代码,可以拆解出归档的结构规则:

  • 文件头:前4字节为魔数 {1,3,3,7}(对应十进制值16974599)
  • 文件计数:紧随魔数的4字节,存储归档内的文件总数(初始创建时为0)
  • 单个文件结构:
    • 4字节:存储文件的字节大小(int类型)
    • 文件名:ASCII编码的字符串,以0字节作为结束标记
    • 文件内容:对应大小的原始字节数据

二、PHP提取逻辑实现(流式处理防内存溢出)

放弃一次性读取整个文件的方式,改用流式分块读写,避免将大文件全部加载到内存中。以下是实现代码:

function extractArchive(string $archivePath, string $outputDir): void {
    // 创建输出目录(递归创建父目录)
    if (!is_dir($outputDir)) {
        mkdir($outputDir, 0755, true);
    }

    $archiveHandle = fopen($archivePath, 'rb');
    if (!$archiveHandle) {
        throw new Exception('无法打开归档文件');
    }

    // 验证归档魔数
    $magicBytes = fread($archiveHandle, 4);
    $magicNumber = unpack('N', $magicBytes)[1];
    if ($magicNumber !== 16974599) {
        fclose($archiveHandle);
        throw new Exception('无效的归档文件格式');
    }

    // 读取归档内的文件总数
    $fileCount = unpack('N', fread($archiveHandle, 4))[1];

    for ($i = 0; $i < $fileCount; $i++) {
        // 读取当前文件的大小
        $fileSize = unpack('N', fread($archiveHandle, 4))[1];

        // 读取文件名(直到遇到终止符\x00)
        $fileName = '';
        while (($byte = fread($archiveHandle, 1)) !== "\x00") {
            $fileName .= $byte;
        }

        // 准备输出文件路径
        $outputFile = rtrim($outputDir, '/') . '/' . $fileName;
        $fileHandle = fopen($outputFile, 'wb');
        if (!$fileHandle) {
            fclose($archiveHandle);
            throw new Exception('无法创建输出文件:' . $fileName);
        }

        // 分块读取并写入文件内容(每次处理4KB,可根据需求调整)
        $bytesProcessed = 0;
        $chunkSize = 4096;
        while ($bytesProcessed < $fileSize) {
            $readLength = min($chunkSize, $fileSize - $bytesProcessed);
            $contentChunk = fread($archiveHandle, $readLength);
            fwrite($fileHandle, $contentChunk);
            $bytesProcessed += $readLength;
        }

        fclose($fileHandle);
    }

    fclose($archiveHandle);
}

三、内存溢出问题的核心解决思路

  • 避免全量加载文件:之前用fread()一次性读取整个文件内容,会将大文件全部存入内存,触发溢出。流式分块处理每次仅加载少量数据,内存占用始终保持在低水平。
  • 拒绝依赖内存限制调整:即使临时调高内存到512MB,遇到更大的归档文件仍会溢出,流式处理才是从根源解决问题的方案。

内容的提问来源于stack exchange,提问作者TKH

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 01:55:17