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

使用Exec函数压缩目录时重复生成两个压缩包的问题求助

压缩目录/文件时重复执行Exec导致覆盖压缩包的问题

使用Exec函数压缩目录或文件时出现异常:完成第一个压缩包生成后,程序会再次对同一目录/文件执行压缩操作,最终覆盖之前生成的压缩包。此前会生成多个压缩包,调整后目前仅会生成两个。

日志信息

2024-05-26 11:58:30 - Lock file is being created: /tmp/32f20e7fedb75ac2b1ebd53199bf7486.lock
2024-05-26 11:58:30 - Running zip command: /home/user/ZIP/file_name-2024-05-26-11-58-30.zip
2024-05-26 11:58:52 - Zip Archive Created Successfully: /home/user/ZIP/file_name-2024-05-26-11-58-30.zip
2024-05-26 11:58:52 - Lock file deleted: /tmp/32f20e7fedb75ac2b1ebd53199bf7486.loc

实现代码

function zipDataUsingSystem($source, $destination, $comment = '') {
    $zipsonuc = [];

    // Check if source directory or file exists
    if (!file_exists($source)) {
        $zipsonuc[] = "Source file or directory does not exist: " . $source;
        return $zipsonuc;
    }

    // Processing and securing file paths
    $sourceRealPath = realpath($source);
    $destinationSafe = escapeshellarg($destination); // Make it safe for command only

    // Specify the path to the lock file
    $lockFile = sys_get_temp_dir() . DIRECTORY_SEPARATOR . md5($sourceRealPath) . '.lock';

    // Write to the log file that the lock file was created
    file_put_contents('/home/user/error.log', date('Y-m-d H:i:s') . " - Lock file is being created: " . $lockFile . "\n", FILE_APPEND);

    // Create and lock lock file
    $fp = fopen($lockFile, 'c');
    if (!$fp) {
        $zipsonuc[] = "Lock file could not be created: " . $lockFile;
        return $zipsonuc;
    }

    // Lock operation
    if (!flock($fp, LOCK_EX | LOCK_NB)) {
        fclose($fp);
        $zipsonuc[] = "Another zip process in progress: " . $source;
        // Write to the log file that it could not be locked.
        file_put_contents('/home/user/error.log', date('Y-m-d H:i:s') . " - Another zip process in progress: " . $source . "\n", FILE_APPEND);
        return $zipsonuc;
    }

    // Check if the target directory exists and create it if necessary
    $destinationDirRealPath = dirname($destination);
    if (!file_exists(KOKYOLU.$destinationDirRealPath)) {
        if (!mkdir(KOKYOLU.$destinationDirRealPath, 0777, true)) {
            $zipsonuc[] = "Could not create target directory: " . $destinationDirRealPath;
            // Release the lock and close the lock file
            flock($fp, LOCK_UN);
            fclose($fp);
            // Write to the log file that the directory could not be created
            file_put_contents('/home/user/error.log', date('Y-m-d H:i:s') . " - Could not create target directory: " . $destinationDirRealPath . "\n", FILE_APPEND);
            return $zipsonuc;
        }
    }

    // Write in the log file that the zip command will be run
    file_put_contents('/home/user/error.log', date('Y-m-d H:i:s') . " - Running zip command: " . $destination . "\n", FILE_APPEND);

    // Create the zip command, go into the source directory and add its contents
    $command = "cd " . escapeshellarg($sourceRealPath) . " && zip -r $destinationSafe .";
    $output = [];
    $return_var = 0;
    exec($command, $output, $return_var);

    // Check the results
    if ($return_var === 0) {
        // Comment adding process
        if ($comment !== '') {
            $comment = escapeshellarg(iconv(mb_detect_encoding($comment, mb_detect_order(), true), "UTF-8", $comment));
            $commentCommand = "zip -z $destinationSafe <<< $comment";
            exec($commentCommand);
        }

        // Remove single quotes in original filename
        $destinationClean = str_replace("'", "", $destination);

        $zipsonuc[] = "Zip Archive Created Successfully";
        $zipsonuc["dosya_adi"] = $destinationClean;

        // Write to the log file that the zip process was completed successfully.
        file_put_contents('/home/user/error.log', date('Y-m-d H:i:s') . " - Zip Archive Created Successfully: " . $destinationClean . "\n", FILE_APPEND);

    } else {
        $zipsonuc[] = "Zip Archive Could Not Be Created Due to an Error: " . implode("<br>", $output);
        // Write the error message to the log file
        file_put_contents('/home/user/error.log', date('Y-m-d H:i:s') . " - Zip Archive Could Not Be Created Due to an Error: " . implode(", ", $output) . "\n", FILE_APPEND);
    }

    // Release the lock and close the lock file
    flock($fp, LOCK_UN);
    fclose($fp);
    unlink($lockFile); // Delete lock file

    // Write to the log file that the lock file has been deleted
    file_put_contents('/home/user/error.log', date('Y-m-d H:i:s') . " - Lock file deleted: " . $lockFile . "\n", FILE_APPEND);

    return $zipsonuc;
}

排查与解决

1. 锁文件异常分析

日志最后一行显示删除的锁文件后缀为.loc而非定义的.lock,需先确认:

  • $lockFile变量在函数执行过程中未被意外修改
  • 日志输出语句未对$lockFile做截断处理

若为笔误,重点检查锁机制有效性:flock依赖一致的锁文件名生效,需确保两次调用时$sourceRealPath完全一致。若$source为相对路径,需强制转换为绝对路径避免因工作目录变化导致锁文件名不同:

// 确保source为绝对路径,可自定义resolveAbsolutePath函数处理相对路径
$sourceRealPath = realpath($source) ?: $source;

2. 添加目标文件存在性检查

在函数开头增加目标压缩包的存在性判断,即使锁机制失效,也能直接跳过重复压缩:

function zipDataUsingSystem($source, $destination, $comment = '') {
    $zipsonuc = [];

    // 新增:检查目标压缩包是否已存在
    if (file_exists($destination)) {
        $zipsonuc[] = "Zip archive already exists: " . $destination;
        file_put_contents('/home/user/error.log', date('Y-m-d H:i:s') . " - Zip archive already exists, skipping: " . $destination . "\n", FILE_APPEND);
        return $zipsonuc;
    }

    // 原有的source存在性检查...
}

3. 排查重复调用源头

检查调用zipDataUsingSystem的入口:

  • 是否存在前端按钮重复点击导致的HTTP请求重复发送
  • 定时任务是否存在重复触发配置
  • 事件监听是否被重复绑定

若存在重复调用,需在入口层做去重处理(如请求幂等性校验)。

4. 优化锁机制可靠性

调整锁文件处理顺序,利用fclose自动释放flock锁的特性,简化代码并确保原子性:

// 替换原锁释放代码块
fclose($fp); // fclose会自动释放flock锁
unlink($lockFile);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 05:44:55