Skip to content

Commit cfb0118

Browse files
authored
Create Zip.php
1 parent 293e1d8 commit cfb0118

File tree

1 file changed

+134
-0
lines changed

1 file changed

+134
-0
lines changed

src/Utils/Zip.php

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
<?php
2+
3+
namespace ZhenMu\Support\Utils;
4+
5+
use Illuminate\Support\Facades\File;
6+
use PhpZip\ZipFile;
7+
8+
class Zip
9+
{
10+
protected $zipFile;
11+
12+
public function __construct()
13+
{
14+
$this->zipFile = new ZipFile();
15+
}
16+
17+
public function pack(string $sourcePath, ?string $filename = null, ?string $targetPath = null)
18+
{
19+
if (!File::exists($sourcePath)) {
20+
throw new \RuntimeException("待解压目录不存在 {$sourcePath}");
21+
}
22+
23+
$filename = $filename ?? File::name($sourcePath);
24+
$targetPath = $targetPath ?? File::dirname($sourcePath);
25+
$targetPath = $targetPath ?: File::dirname($sourcePath);
26+
27+
File::ensureDirectoryExists($targetPath);
28+
29+
$zipFilename = str_contains($filename, '.zip') ? $filename : $filename . '.zip';
30+
$zipFilepath = "{$targetPath}/{$zipFilename}";
31+
32+
while (File::exists($zipFilepath)) {
33+
$basename = File::name($zipFilepath);
34+
$zipCount = count(File::glob("{$targetPath}/{$basename}*.zip"));
35+
36+
$zipFilename = $basename . ($zipCount) . '.zip';
37+
$zipFilepath = "{$targetPath}/{$zipFilename}";
38+
}
39+
40+
// 压缩
41+
$this->zipFile->addDirRecursive($sourcePath, $filename);
42+
$this->zipFile->saveAsFile($zipFilepath);
43+
44+
return $targetPath;
45+
}
46+
47+
public function unpack(string $sourcePath, ?string $dirname = null)
48+
{
49+
try {
50+
// 检测文件类型,只有 zip 文件才进行解压操作
51+
$mimeType = File::mimeType($sourcePath);
52+
} catch (\Throwable $e) {
53+
\info("解压失败 {$e->getMessage()}");
54+
throw new \RuntimeException("解压失败 {$e->getMessage()}");
55+
}
56+
57+
// 获取文件类型(只处理目录和 zip 文件)
58+
$type = match (true) {
59+
default => null,
60+
str_contains($mimeType, 'directory') => 1,
61+
str_contains($mimeType, 'zip') => 2,
62+
};
63+
64+
if (is_null($type)) {
65+
\info("unsupport mime type $mimeType");
66+
throw new \RuntimeException("unsupport mime type $mimeType");
67+
}
68+
69+
// 确保解压目标目录存在
70+
$targetPath = $targetPath ?? storage_path('app/extensions/.tmp');
71+
if (empty($targetPath)) {
72+
throw new \RuntimeException("targetPath cannot be empty.");
73+
}
74+
75+
if (!is_dir($targetPath)) {
76+
if (!File::ensureDirectoryExists($targetPath)) {
77+
throw new \RuntimeException("mkdir {$targetPath} failed.");
78+
}
79+
}
80+
81+
// 清空目录,避免留下其他插件的文件
82+
File::cleanDirectory($targetPath);
83+
84+
// 目录无需解压操作,将原目录拷贝到临时目录中
85+
if ($type == 1) {
86+
File::copyDirectory($sourcePath, $targetPath);
87+
88+
// 确保目录解压层级是插件目录顶层
89+
$this->ensureDoesntHaveSubdir($targetPath);
90+
91+
return $targetPath;
92+
}
93+
94+
if ($type == 2) {
95+
// 解压
96+
$zipFile = $this->zipFile->openFile($sourcePath);
97+
$zipFile->extractTo($targetPath);
98+
99+
// 确保目录解压层级是插件目录顶层
100+
$this->ensureDoesntHaveSubdir($targetPath);
101+
102+
// 解压到指定目录
103+
return $targetPath;
104+
}
105+
106+
return null;
107+
}
108+
109+
public function ensureDoesntHaveSubdir(string $sourcePath)
110+
{
111+
$targetPath = $sourcePath ?? storage_path('app/extensions/.tmp');
112+
113+
$pattern = sprintf("%s/*", rtrim($sourcePath, DIRECTORY_SEPARATOR));
114+
$files = File::glob($pattern);
115+
116+
if (count($files) > 1) {
117+
return false;
118+
}
119+
120+
$tmpDir = $targetPath . '-subdir';
121+
if (File::ensureDirectoryExists($tmpDir)) {
122+
throw new \RuntimeException("mkdir {$tmpDir} failed.");
123+
}
124+
125+
$firstEntryname = File::name(current($files));
126+
127+
File::copyDirectory($targetPath . "/{$firstEntryname}", $tmpDir);
128+
File::cleanDirectory($targetPath);
129+
File::copyDirectory($tmpDir, $targetPath);
130+
File::deleteDirectory($tmpDir);
131+
132+
return $targetPath;
133+
}
134+
}

0 commit comments

Comments
 (0)