在没有 exec 的情况下创建 zip 或 tar.gz 存档

Posted

技术标签:

【中文标题】在没有 exec 的情况下创建 zip 或 tar.gz 存档【英文标题】:Creating zip or tar.gz archive without exec 【发布时间】:2011-08-10 01:36:28 【问题描述】:

有没有什么安全的方法可以在不使用 exec 命令的情况下从 php 创建 zip 存档或 tar.gz?

谢谢

【问题讨论】:

您正在寻找 PEAR 档案。有一个 zip 和 tar 格式的版本。 【参考方案1】:

如果你想创建 tar.gz 并且你使用的是 PHP 5.3+,你可以使用PharData class:

try

    $a = new PharData('archive.tar');

    // ADD FILES TO archive.tar FILE
    $a->addFile('data.xls');
    $a->addFile('index.php');

    // COMPRESS archive.tar FILE. COMPRESSED FILE WILL BE archive.tar.gz
    $a->compress(Phar::GZ);

    // NOTE THAT BOTH FILES WILL EXISTS. SO IF YOU WANT YOU CAN UNLINK archive.tar
    unlink('archive.tar');
 
catch (Exception $e) 

    echo "Exception : " . $e;

【讨论】:

在取消链接 archive.tar 之前确保文件仍未打开 - 在尝试示例时遇到错误 - unset($a) 成功了^^ 如果tar 存档在多个用户之间共享,->addFile 是并发的还是会发生这种情况?当用户 A 将文件添加到存档时,另一个用户 B 添加了一个文件,这会破坏整个 TAR 存档或导致 A 添加的文件被 B 添加的文件覆盖而丢失的情况?跨度> 请注意,您不能添加符号链接(symlinks),它们总是被取消引用。见bugs.php.net/bug.php?id=65332 谢谢,值得注意的是,在 Drupal 8 中,.tar 扩展名目前已被列入黑名单。网站遇到意外错误。请稍后再试。 TYPO3\PharStreamWrapper\Exception:Drupal\Core\Security\PharExtensionInterceptor->assert() 中“phar:///app/docroot/export.tar/”中的意外文件扩展名(core/lib/Drupal/Core/ 的第 38 行)安全/PharExtensionInterceptor.php)。【参考方案2】:

您可以使用 PHP 的 Zip 类来创建 zip 文件,并使用 ZLib 来创建 gzip 文件。

创建.zip 文件:

$zip = new ZipArchive();
$res = $zip->open('test.zip', ZipArchive::CREATE);
$zip->addFromString('test.txt', 'file content goes here');
$zip->addFile('data.txt', 'entryname.txt');
$zip->close();

创建.gz 文件:

$file = "test.txt";
$gzfile = "test.gz";
$fp = gzopen($gzfile, 'w9'); // w == write, 9 == highest compression
gzwrite($fp, file_get_contents($file));
gzclose($fp);

【讨论】:

当然!只需查看上面链接的文档 - 很容易理解。【参考方案3】:

改用系统命令

http://php.net/manual/en/function.system.php

注意关于如何防止恶意输入的注释部分。

编辑:使用 ZipArchive 类通过 open() 命令创建一个新的 zip http://www.php.net/manual/en/class.ziparchive.php

【讨论】:

谢谢,但我试图远离外壳,因为在许多共享主机中,这些功能被禁用。【参考方案4】:

有一种非常简单的方法可以直接在 PHP 中创建 Tar-archives - 您可以在 LGPL 下的 Dennis Wronka 在sourceforge 找到一个 CLASS,这样您也可以在商业脚本中使用。

<?php
/**
 * @package PHPClassCollection
 * @subpackage Tar
 * @link classes
 * @author Dennis Wronka <reptiler@users.sourceforge.net>
 */
/**
 * @package PHPClassCollection
 * @subpackage Tar
 * @link classes
 * @author Dennis Wronka <reptiler@users.sourceforge.net>
 * @version 1.1
 * @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html LGPL 2.1
 */
class tar

/**
 * The name of the tar-file to create.
 *
 * @var string
 */
var $filename;
/**
 * The list of files to add to the archive.
 *
 * @var array
 */
var $filelist=array();

/**
 * Constructor
 *
 * @param string $filename
 */
function tar($filename)

    $this->filename=$filename;


/**
 * Add a file.
 *
 * @param string $filename
 */
function add($filename)

    if ((file_exists($filename)) && (is_readable($filename)))
    
        $this->filelist[]=$filename;
    


/**
 * Write the tar-file.
 *
 * @return bool
 */
function write()

    sort($this->filelist);
    $tarfile=@fopen($this->filename,'w');
    if ($tarfile==false)
    
        return false;
    
    for ($x=0;$x<count($this->filelist);$x++)
    
        $filename=$this->filelist[$x];
        if ((is_dir($this->filelist[$x])) && (substr($this->filelist[$x],-1)!='/'))
        
            $filename.='/';
        
        while (strlen($filename)<100)
        
            $filename.=chr(0);
        
        $permissions=sprintf('%o',fileperms($this->filelist[$x])).chr(0);
        while (strlen($permissions)<8)
        
            $permissions='0'.$permissions;
        
        $userid=sprintf('%o',fileowner($this->filelist[$x])).chr(0);
        while (strlen($userid)<8)
        
            $userid='0'.$userid;
        
        $groupid=sprintf('%o',filegroup($this->filelist[$x])).chr(0);
        while (strlen($groupid)<8)
        
            $groupid='0'.$groupid;
        
        if (is_dir($this->filelist[$x]))
        
            $filesize='0'.chr(0);
        
        else
        
            $filesize=sprintf('%o',filesize($this->filelist[$x])).chr(0);
        
        while (strlen($filesize)<12)
        
            $filesize='0'.$filesize;
        
        $modtime=sprintf('%o',filectime($this->filelist[$x])).chr(0);
        $checksum='        ';
        if (is_dir($this->filelist[$x]))
        
            $indicator=5;
        
        else
        
            $indicator=0;
        
        $linkname='';
        while (strlen($linkname)<100)
        
            $linkname.=chr(0);
        
        $ustar='ustar  '.chr(0);
        if (function_exists('posix_getpwuid'))
        
            $user=posix_getpwuid(octdec($userid));
            $user=$user['name'];
        
        else
        
            $user='';
        
        while (strlen($user)<32)
        
            $user.=chr(0);
        
        if (function_exists('posix_getgrgid'))
        
            $group=posix_getgrgid(octdec($groupid));
            $group=$group['name'];
        
        else
        
            $group='';
        
        while (strlen($group)<32)
        
            $group.=chr(0);
        
        $devmajor='';
        while (strlen($devmajor)<8)
        
            $devmajor.=chr(0);
        
        $devminor='';
        while (strlen($devminor)<8)
        
            $devminor.=chr(0);
        
        $prefix='';
        while (strlen($prefix)<155)
        
            $prefix.=chr(0);
        
        $header=$filename.$permissions.$userid.$groupid.$filesize.$modtime.$checksum.$indicator.$linkname.$ustar.$user.$group.$devmajor.$devminor.$prefix;
        while (strlen($header)<512)
        
            $header.=chr(0);
        
        $checksum=0;
        for ($y=0;$y<strlen($header);$y++)
        
            $checksum+=ord($header[$y]);
        
        $checksum=sprintf('%o',$checksum).chr(0).' ';
        while (strlen($checksum)<8)
        
            $checksum='0'.$checksum;
        
        $header=$filename.$permissions.$userid.$groupid.$filesize.$modtime.$checksum.$indicator.$linkname.$ustar.$user.$group.$devmajor.$devminor.$prefix;
        while (strlen($header)<512)
        
            $header.=chr(0);
        
        fwrite($tarfile,$header);
        if ($indicator==0)
        
            $contentfile=fopen($this->filelist[$x],'r');
            $data=fread($contentfile,filesize($this->filelist[$x]));
            while (strlen($data)%512!=0)
            
                $data.=chr(0);
            
            fwrite($tarfile,$data);
        
    
    fclose($tarfile);
    return true;


?>

我还有一个简单纯正的 ZIP PHP 类:

<?php /* 110 Lines */
/*
//  ZIP File Creation Class - http://www.pkware.com/appnote.txt

//  Download ZIP File
    include_once 'zip.inc.php';
    $zipfile = new zipfile();
    $fileonserver = 'path/to/file/oldfilename.txt';
    $fileinarchive = 'newfilename.txt';
    $zipfile->addFile(file_get_contents($fileonserver), $fileinarchive);
    header('Content-type: application/octet-stream');
    header('Content-disposition: attachment; filename=archive.zip');
    echo $zipfile->file();

//  Save ZIP File To Server
    include_once 'zip.inc.php';
    $zipfile = new zipfile();
    $fileonserver = 'path/to/file/oldfilename.txt';
    $fileinarchive = 'newfilename.txt';
    $zipfile->addFile(file_get_contents($fileonserver), $fileinarchive);
    $contents = $zipfile->file();
    file_put_contents('archive.zip', $contents);
*/

$zipfile = new zipfile();
$zipfile->addFile(file_get_contents(dirname(__FILE__).'/zip.inc.php'), 'zip.inc.php');
file_put_contents('archive.zip', $zipfile->file());

class zipfile  
  var $datasec = array(); 
  var $ctrl_dir = array(); 
  var $eof_ctrl_dir = "\x50\x4b\x05\x06\x00\x00\x00\x00"; 
  var $old_offset = 0; 
  function unix2DosTime($unixtime = 0)  
    $timearray = ($unixtime == 0) ? getdate() : getdate($unixtime); 
    if ($timearray['year'] < 1980)  
      $timearray['year'] = 1980; 
      $timearray['mon'] = 1; 
      $timearray['mday'] = 1; 
      $timearray['hours'] = 0; 
      $timearray['minutes'] = 0; 
      $timearray['seconds'] = 0;
     
    return 
      (($timearray['year'] - 1980) << 25) | 
      ($timearray['mon'] << 21) | 
      ($timearray['mday'] << 16) | 
      ($timearray['hours'] << 11) | 
      ($timearray['minutes'] << 5) | 
      ($timearray['seconds'] >> 1);
  
  function addFile($data, $name, $time = 0)  
    $name = str_replace('\\', '/', $name); 
    $dtime = dechex($this->unix2DosTime($time)); 
    $hexdtime = '\x' . $dtime[6] . $dtime[7] . 
                '\x' . $dtime[4] . $dtime[5] . 
                '\x' . $dtime[2] . $dtime[3] . 
                '\x' . $dtime[0] . $dtime[1]; 
    eval('$hexdtime = "' . $hexdtime . '";'); 
    $fr = "\x50\x4b\x03\x04"; 
    $fr .= "\x14\x00"; 
    $fr .= "\x00\x00"; 
    $fr .= "\x08\x00"; 
    $fr .= $hexdtime; 
    $unc_len = strlen($data); 
    $crc = crc32($data); 
    $zdata = gzcompress($data); 
    $zdata = substr(substr($zdata, 0, strlen($zdata) - 4), 2); 
    $c_len = strlen($zdata); 
    $fr .= pack('V', $crc); 
    $fr .= pack('V', $c_len); 
    $fr .= pack('V', $unc_len); 
    $fr .= pack('v', strlen($name)); 
    $fr .= pack('v', 0); 
    $fr .= $name; 
    $fr .= $zdata; 
    $this -> datasec[] = $fr; 
    $cdrec = "\x50\x4b\x01\x02"; 
    $cdrec .= "\x00\x00"; 
    $cdrec .= "\x14\x00"; 
    $cdrec .= "\x00\x00"; 
    $cdrec .= "\x08\x00"; 
    $cdrec .= $hexdtime; 
    $cdrec .= pack('V', $crc); 
    $cdrec .= pack('V', $c_len); 
    $cdrec .= pack('V', $unc_len); 
    $cdrec .= pack('v', strlen($name) ); 
    $cdrec .= pack('v', 0 ); 
    $cdrec .= pack('v', 0 ); 
    $cdrec .= pack('v', 0 ); 
    $cdrec .= pack('v', 0 ); 
    $cdrec .= pack('V', 32 ); 
    $cdrec .= pack('V', $this -> old_offset ); 
    $this -> old_offset += strlen($fr); 
    $cdrec .= $name; 
    $this -> ctrl_dir[] = $cdrec;
  
  function file()  
    $data = implode('', $this -> datasec); 
    $ctrldir = implode('', $this -> ctrl_dir); 
    return $data . $ctrldir . 
      $this -> eof_ctrl_dir . 
      pack('v', sizeof($this -> ctrl_dir)) . 
      pack('v', sizeof($this -> ctrl_dir)) . 
      pack('V', strlen($ctrldir)) . 
      pack('V', strlen($data)) . 
      "\x00\x00";
  
 

【讨论】:

【参考方案5】:

你也可以使用PHP compression stream wrappers在一行中压缩和解压文件:

// Reads file.txt, passes it throyuh the zlib wrapper and writes the archive file.txt.gz
copy('data.xls', 'compress.zlib://' . 'data.xls.gz');

// Reads the archive file.txt.gz using zlib wrapper, writes uncompressed file
copy('compress.zlib://' . 'data.xls.gz', 'data.xls');

【讨论】:

以上是关于在没有 exec 的情况下创建 zip 或 tar.gz 存档的主要内容,如果未能解决你的问题,请参考以下文章

如何在Linux下创建与解压zip, tar, tar.gz和tar.bz2文件

如何在Linux下创建与解压zip, tar, tar.gz和tar.bz2文件

已解决:解压Python-3.6.1.tar.xz提示tar (child): xz:无法 exec: 没有那个文件或目录

如何在没有一些文件夹的情况下创建 tar 存档?

如何在没有顶层目录的情况下创建 tar 文件

Linux下tar 文件的打包与解压