在没有 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: 没有那个文件或目录