/**
* @description: zip压缩,压缩/解压缩
*/
class ZIPCompress
{
/**
* @description:
* @var ZipArchive
*/
protected $zip;
public function __construct($filename = '', $flag = 0)
{
if ($filename) {
$this->open($filename, $flag);
}
}
public function __destruct()
{
// 关闭打开或创建的存档并保存更改。失败返回false
$this->zip->close();
}
/**
* @description: 打开新的或现有的zip存档以进行读取,写入或修改,成功返回true,失败将返回指定错误代码
* @param string $filename 文件名
* @param int $flag
* ZipArchive::OVERWRITE 8 打开新的压缩包,覆盖现有文件
* ZipArchive::CREATE 1 如果不存在创建一个新压缩包
* ZipArchive::RDONLY 16 只读模式打开压缩包
* ZipArchive::EXCL 2 如果压缩包已经存在,则出错
* ZipArchive::CHECKCONS 4 对压缩包执行额外的一致性检查,如果失败则显示错误
* @return int|true
*/
public function open($filename, $flag = 0)
{
$this->zip = new ZipArchive;
$res = $this->zip->open($filename, $flag);
if ($res !== true) {
throw new Exception($this->zipErorr($res));
}
}
/**
* @description: 给zip文件加密
* @param string $password 密码
* @return {*}
*/
public function encryption($password)
{
return $this->zip->setPassword($password);
}
/**
* @description: 将文件解压缩到指定目录,压缩包中的中文解决不了
* @param string $dir 目录,默认当前目录
* @return bool
*/
public function uncompress($dir = './')
{
!is_dir($dir) && mkdir($dir, 0777, true);
$dir = realpath($dir);
return $this->zip->extractTo($dir);
}
/**
* @description: 自定义解压缩,支持中文,支持递归解压缩
* @param string $dir 解压后的目录,默认当前目录
* @return bool
*/
public function uncompress_custom($dir = './')
{
!is_dir($dir) && mkdir($dir, 0777, true);
$dir = str_replace('\\', '/', realpath($dir)) . '/';
// 获取压缩包中文档数量
$count = version_compare(PHP_VERSION, '7.2') > -1 ? $this->zip->count() : $this->zip->numFiles;
$encoding = ['utf-8', 'gbk', 'big5', 'cp936'];
for ($i = 0; $i < $count; $i++) {
// 根据索引获取文件详细信息,解决中文乱码
$files = $this->zip->statIndex($i, ZipArchive::FL_ENC_RAW);
$encode = mb_detect_encoding($files['name'], $encoding);
$filename = iconv($encode, 'utf-8//ignore', $files['name']);
$file = $dir . $filename;
if ($filename[-1] == '/') {
!is_dir($file) && mkdir($file, 0777, true);
} else {
$path = dirname($file);
$path && !is_dir($path) && mkdir($path, 0777, true);
file_put_contents($file, $this->zip->getFromIndex($i));
$ext = pathinfo($file, PATHINFO_EXTENSION);
if ($ext == 'zip') {
$zip = new self($file);
$dir1 = str_replace('.' . $ext, '', $file);
!is_dir($dir1) && mkdir($dir1, 0777, true);
$zip->uncompress_custom($dir1);
unset($zip);
@unlink($file);
}
}
}
}
/**
* @description: 压缩指定目录或指定文件
* @param string $dir 要压缩的目录,默认为当前目录
* @param array $files 要压缩的文件数组,[['name'=>'','path'=>'']]$files指定时,$dir参数不生效
* @param string $gen_dir 生成压缩包位置,默认当前位置
* @param string $name 压缩后的文件名,默认自动生成
* @return bool
*/
public function compress($dir = './', $files = [], $gen_dir = './', $name = null)
{
!is_dir($gen_dir) && mkdir(iconv('utf-8', 'gbk', $gen_dir), 0777, true);
$zipname = $gen_dir . ($name ?: (time() . bin2hex(random_bytes(8)))) . '.zip';
$this->open($zipname, \ZipArchive::CREATE);
if ($files) {
foreach ($files as $file) {
if (empty($file['path']) || !file_exists($file['path'])) continue;
$this->zip->addFile($file['path'], $file['name']);
}
} else {
$this->_dir_zip($this->zip, $dir);
}
return $zipname;
}
/**
* @description: 递归压缩目录
* @param object $zip zip对象
* @param string $dir 要压缩的目录
* @param string $zip_dir 在压缩包中要创建的目录
* @return {*}
*/
private function _dir_zip($zip, $dir, $zip_dir = '')
{
$dir = str_replace('\\', '/', realpath($dir)) . '/';
if ($files = opendir($dir)) {
while (($file = readdir($files)) !== false) {
if ($file == '.' || $file == '..') continue;
$path = $dir . $file;
if (is_dir($path)) {
$temp_dir = $zip_dir . $file . '/';
// 添加一个空目录,失败时返回false
$zip->addEmptyDir($temp_dir);
$this->_dir_zip($zip, $path, $temp_dir);
} else {
$zip->addFile($zip_dir . $file, $path);
}
}
}
}
/**
* @description: 获取zip错误
* @param int $error 错误编码
* @return {*}
*/
protected function zipErorr($error)
{
return [
ZipArchive::ER_EXISTS => '文件已存在',
ZipArchive::ER_INCONS => 'zip压缩包不一致',
ZipArchive::ER_INVAL => '无效的论点',
ZipArchive::ER_MEMORY => 'Malloc失败',
ZipArchive::ER_NOENT => '文件不存在',
ZipArchive::ER_NOZIP => '不是zip压缩包',
ZipArchive::ER_OPEN => '无法打开文件',
ZipArchive::ER_READ => '读取错误',
ZipArchive::ER_SEEK => '寻址错误',
][$error] ?? '';
}
}