使用PHP如何创建一个.gz文件?

64

我想使用PHP在我的服务器上对文件进行gzip压缩。是否有人有一个例子,可以输入一个文件并输出一个压缩文件?

10个回答

105

这段代码很有效

// Name of the file we're compressing
$file = "test.txt";

// Name of the gz file we're creating
$gzfile = "test.gz";

// Open the gz file (w9 is the highest compression)
$fp = gzopen ($gzfile, 'w9');

// Compress the file
gzwrite ($fp, file_get_contents($file));

// Close the gz file and we're done
gzclose($fp);

16
不幸的是,这可能会将整个文件读入内存,可能会在处理大文件时达到PHP的内存限制。 :-( - Simon East
w9是最高的压缩级别吗?哪个是最低的压缩级别,仅用于在.gz文件中打包数据? - user1642018
@SimonEast说得对。这个函数在处理大文件时会撑满你的内存限制(而且你知道吗?gzip被用来压缩那种大文件)。正确答案是上面那个,而不是这个。 - Max Cuttins

104

其他答案在压缩期间将整个文件加载到内存中,这会导致在大文件上出现“内存不足”错误。下面的函数应该更可靠,因为它以512kb的块读取和写入文件。

/**
 * GZIPs a file on disk (appending .gz to the name)
 *
 * From https://dev59.com/LW025IYBdhLWcg3wVkYR
 * Based on function by Kioob at:
 * http://www.php.net/manual/en/function.gzwrite.php#34955
 * 
 * @param string $source Path to file that should be compressed
 * @param integer $level GZIP compression level (default: 9)
 * @return string New filename (with .gz appended) if success, or false if operation fails
 */
function gzCompressFile($source, $level = 9){ 
    $dest = $source . '.gz'; 
    $mode = 'wb' . $level; 
    $error = false; 
    if ($fp_out = gzopen($dest, $mode)) { 
        if ($fp_in = fopen($source,'rb')) { 
            while (!feof($fp_in)) 
                gzwrite($fp_out, fread($fp_in, 1024 * 512)); 
            fclose($fp_in); 
        } else {
            $error = true; 
        }
        gzclose($fp_out); 
    } else {
        $error = true; 
    }
    if ($error)
        return false; 
    else
        return $dest; 
} 

更新: Gerben发布了一个改进版本的函数,它更加清晰,并且在出错时使用异常而不是返回false。请参见https://dev59.com/LW025IYBdhLWcg3wVkYR#56140427


完美!正是我所需要的。 - Clox
3
非常好的一段代码。我使用上面的代码创建了一个反向操作来解压缩文件。这段代码运行相当快。 - user3759531

23

另外,你可以使用php的包装工具,其中包括压缩功能。只需对代码进行最小的更改,即可在gzip、bzip2或zip之间切换。

$input = "test.txt";
$output = $input.".gz";

file_put_contents("compress.zlib://$output", file_get_contents($input));

compress.zlib://更改为compress.zip://以进行zip压缩(有关zip压缩,请参见此答案的评论),或将其更改为compress.bzip2://以进行bzip2压缩。


我认为compress.zip://不支持压缩。请参见http://www.php.net/manual/en/wrappers.compression.php上的说明。 - Alex
@Alex 是的,看起来你是正确的,zip://包装器不支持向已存在的文件写入或追加。 - Carlos Campderrós

7

使用gzencode()的简单一行代码:

gzencode(file_get_contents($file_name));

1
太好了,感谢您的指引。这条有用的回答让我了解到了一整套 Zlib函数:https://www.php.net/manual/en/ref.zlib.php - Rounin

4

这是一个改进版。我摆脱了所有嵌套的if/else语句,从而降低了圈复杂度,通过异常处理来实现更好的错误处理,而不是跟踪布尔错误状态,还有一些类型提示,并且如果文件已经具有gz扩展名,则退出。虽然代码行数有点增加,但可读性大大提高。


/**
 * Compress a file using gzip
 *
 * Rewritten from Simon East's version here:
 * https://dev59.com/LW025IYBdhLWcg3wVkYR#22754032
 *
 * @param string $inFilename Input filename
 * @param int    $level      Compression level (default: 9)
 *
 * @throws Exception if the input or output file can not be opened
 *
 * @return string Output filename
 */
function gzcompressfile(string $inFilename, int $level = 9): string
{
    // Is the file gzipped already?
    $extension = pathinfo($inFilename, PATHINFO_EXTENSION);
    if ($extension == "gz") {
        return $inFilename;
    }

    // Open input file
    $inFile = fopen($inFilename, "rb");
    if ($inFile === false) {
        throw new \Exception("Unable to open input file: $inFilename");
    }

    // Open output file
    $gzFilename = $inFilename.".gz";
    $mode = "wb".$level;
    $gzFile = gzopen($gzFilename, $mode);
    if ($gzFile === false) {
        fclose($inFile);
        throw new \Exception("Unable to open output file: $gzFilename");
    }

    // Stream copy
    $length = 512 * 1024; // 512 kB
    while (!feof($inFile)) {
        gzwrite($gzFile, fread($inFile, $length));
    }

    // Close files
    fclose($inFile);
    gzclose($gzFile);

    // Return the new filename
    return $gzFilename;
}

谢谢Gerben。是的,你的版本更简洁,我同意在这里使用异常处理更好。 - Simon East
非常棒的工作,非常感谢。 - J.Z.
太棒了,非常感谢你的辛勤工作。 - J.Z.

3
如果您只想解压缩一个文件,这个方法能够正常运行而且不会因为内存问题而产生错误。
$bytes = file_put_contents($destination, gzopen($gzip_path, r));

3

对许多人来说可能很明显,但如果你的系统启用了任何程序执行函数 (execsystemshell_exec) 中的一个,你可以使用它们来简单地 gzip 文件。

exec("gzip ".$filename);

注意:一定要正确地对$filename变量进行清理,特别是如果它来自用户输入(不仅仅如此)。它可能被用于运行任意命令,例如包含类似my-file.txt && anothercommand(或者my-file.txt; anothercommand)的内容。


在大多数托管平台上,exec被锁定,并且即使使用exec也存在安全风险。 - Wranorn
1
@Wranorn 这就是为什么我指出“如果您的系统启用了任何程序执行功能”,我应该写“在您的托管平台上”吗?至于安全性,如果您不将用户输入传递给此函数,我不确定风险是什么。 PHP文档中唯一的警告是在将用户提供的数据传递给函数时使用escapeshellarg()escapeshellcmd() - sylbru
谢谢,这是针对自定义项目的Linux服务器的最佳答案。如果此函数在项目内部使用且没有用户交互,例如通过cron - 它是100%安全的。 - realmag777

0
这个函数由@Simon East和@Gerben共同完成,它存储了整个文件系统的路径,例如"/var/www/sites/example.com/path/to/my/file.gz",而不仅仅是"my/file.gz"。
只有在尝试提取它之后,你才会知道。

0

复制('file.txt', 'compress.zlib://' . 'file.txt.gz'); 请参阅文档


0

为需要的任何人压缩文件夹

function gzCompressFile($source, $level = 9)
{
    $tarFile = $source . '.tar';

    if (is_dir($source)) {
        $tar = new PharData($tarFile);
        $files = scandir($source);
        foreach ($files as $file) {
            if (is_file($source . '/' . $file)) {
                $tar->addFile($source . '/' . $file, $file);
            }
        }
    }

    $dest = $tarFile . '.gz';
    $mode = 'wb' . $level;
    $error = false;
    if ($fp_out = gzopen($dest, $mode)) {
        if ($fp_in = fopen($tarFile, 'rb')) {
            while (!feof($fp_in))
                gzwrite($fp_out, fread($fp_in, 1024 * 512));
            fclose($fp_in);
        } else {
            $error = true;
        }
        gzclose($fp_out);
        unlink($tarFile);
    } else {
        $error = true;
    }
    if ($error)
        return false;
    else
        return $dest;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接