├── MyUpload
├── functions.php
└── Plugin.php
└── README.md
/MyUpload/functions.php:
--------------------------------------------------------------------------------
1 | uploadHandle = array('MyUpload_Plugin', 'uploadHandle');
31 | Typecho_Plugin::factory('Widget_Upload')->modifyHandle = array('MyUpload_Plugin', 'modifyHandle');
32 | }
33 |
34 | /**
35 | * 禁用插件方法,如果禁用失败,直接抛出异常
36 | *
37 | * @static
38 | * @access public
39 | * @return void
40 | */
41 | public static function deactivate()
42 | {
43 | // TODO: Implement deactivate() method.
44 | }
45 |
46 | /**
47 | * 获取插件配置面板
48 | *
49 | * @static
50 | * @access public
51 | * @param Typecho_Widget_Helper_Form $form 配置面板
52 | * @return void
53 | */
54 | public static function config(Typecho_Widget_Helper_Form $form)
55 | {
56 | $compress_larger = new Typecho_Widget_Helper_Form_Element_Text('compress_larger', NULL, '50',
57 | _t('图片压缩大小阈值'), _t('当图片大小超过此值(单位:KB)时,对图片进行压缩。(仅压缩 jpg 和 png )
58 | 如果使用云主机,须安装 jpegoptim 和 pngquant,不要填 TinyPNG API Key!
59 | Ubuntu 系统下安装方法:sudo apt install jpegoptim pngquant'));
60 | $form->addInput($compress_larger);
61 |
62 | $apiKey = new Typecho_Widget_Helper_Form_Element_Text('apiKey', NULL, NULL,
63 | _t('TinyPNG API Key'),
64 | _t('如果使用虚拟主机,采用图片远程压缩。须先到
65 | https://tinypng.com/developers 注册。
66 | 图片远程压缩速度很慢,请耐心等待。'));
67 | $form->addInput($apiKey);
68 |
69 | $upload_subdir = new Typecho_Widget_Helper_Form_Element_Radio('upload_subdir',
70 | array('year' => '年目录',
71 | 'month' => '年月目录',
72 | 'origin' => 'Typecho默认'),
73 | 'origin', _t('图片上传路径'), _t('前两项会使用优化的图片重命名策略
74 | GitHub: https://github.com/jlice/typecho-MyUpload'));
75 | $form->addInput($upload_subdir);
76 | }
77 |
78 | /**
79 | * 个人用户的配置面板
80 | *
81 | * @access public
82 | * @param Typecho_Widget_Helper_Form $form
83 | * @return void
84 | */
85 | public static function personalConfig(Typecho_Widget_Helper_Form $form)
86 | {
87 | // TODO: Implement personalConfig() method.
88 | }
89 |
90 | private static function makeUploadDir($path)
91 | {
92 | $path = preg_replace("/\\\+/", '/', $path);
93 | $current = rtrim($path, '/');
94 | $last = $current;
95 |
96 | while (!is_dir($current) && false !== strpos($path, '/')) {
97 | $last = $current;
98 | $current = dirname($current);
99 | }
100 |
101 | if ($last == $current) {
102 | return true;
103 | }
104 |
105 | if (!@mkdir($last)) {
106 | return false;
107 | }
108 |
109 | $stat = @stat($last);
110 | $perms = $stat['mode'] & 0007777;
111 | @chmod($last, $perms);
112 |
113 | return self::makeUploadDir($path);
114 | }
115 |
116 | private static function getSafeName(&$name)
117 | {
118 | $name = str_replace(array('"', '<', '>'), '', $name);
119 | $name = str_replace('\\', '/', $name);
120 | $name = false === strpos($name, '/') ? ('a' . $name) : str_replace('/', '/a', $name);
121 | $info = pathinfo($name);
122 | $name = substr($info['basename'], 1);
123 |
124 | return isset($info['extension']) ? strtolower($info['extension']) : '';
125 | }
126 |
127 | /**
128 | * 优化的图片重命名策略
129 | *
130 | * @return string
131 | */
132 | public static function generateFilename()
133 | {
134 | return sprintf('%s', base_convert(uniqid(), 16, 36));
135 | }
136 |
137 | /**
138 | * 压缩图片
139 | *
140 | * @param $path
141 | * @throws Exception
142 | */
143 | public static function compressImage(&$path)
144 | {
145 | $compress_larger = htmlspecialchars(Typecho_Widget::widget('Widget_Options')->plugin('MyUpload')->compress_larger);
146 | $compress_larger = is_numeric($compress_larger) ? intval($compress_larger) * 1024 : 50 * 1024;
147 |
148 | $ext = strtolower(pathinfo($path)['extension']);
149 |
150 | // 如果是图片,而且图片较大,先对其进行压缩
151 | if (in_array($ext, self::IMG_EXT) && filesize($path) > $compress_larger) {
152 | if ($ext === "png") {
153 | compress_png_inplace($path);
154 | } elseif ($ext === "jpg" || $ext === "jpeg") {
155 | compress_jpg_inplace($path);
156 | }
157 |
158 | // 清除文件信息缓存,以获得正确的图片大小
159 | clearstatcache();
160 | }
161 | }
162 |
163 | /**
164 | * 远程压缩图片
165 | *
166 | * @param $bytes
167 | * @param $client
168 | * @return bool
169 | * @throws Typecho_Http_Client_Exception
170 | */
171 | public static function remoteCompressImage($bytes, $client) {
172 | if (!$client) return false;
173 | $client->setData($bytes);
174 | $client->setTimeout(60);
175 | $responseBody = $client->send(self::TINIFY_URL);
176 | if ($responseBody && ($responseBody = json_decode($responseBody))) {
177 | if ($picUrl = $responseBody->output->url) {
178 | $client->setMethod(Typecho_Http_Client::METHOD_GET);
179 | return $client->send($picUrl);
180 | }
181 | }
182 | return false;
183 | }
184 |
185 | /**
186 | * 上传文件处理函数,如果需要实现自己的文件哈希或者特殊的文件系统,请在options表里把uploadHandle改成自己的函数
187 | *
188 | * @access public
189 | * @param array $file 上传的文件
190 | * @return mixed
191 | * @throws Exception
192 | */
193 | public static function uploadHandle($file)
194 | {
195 | $ext = self::getSafeName($file['name']);
196 |
197 | if (!self::checkFileType($ext) || Typecho_Common::isAppEngine()) {
198 | return false;
199 | }
200 |
201 | $date = new Typecho_Date();
202 |
203 | $subdir_opt = htmlspecialchars(Typecho_Widget::widget('Widget_Options')->plugin('MyUpload')->upload_subdir);
204 | if ($subdir_opt === 'root') {
205 | $upload_subdir = '';
206 | } elseif ($subdir_opt === 'year') {
207 | $upload_subdir = '/' . $date->year;
208 | } else {
209 | $upload_subdir = '/' . $date->year . '/' . $date->month;
210 | }
211 |
212 |
213 | $path = Typecho_Common::url(defined('__TYPECHO_UPLOAD_DIR__') ? __TYPECHO_UPLOAD_DIR__ : self::UPLOAD_DIR,
214 | defined('__TYPECHO_UPLOAD_ROOT_DIR__') ? __TYPECHO_UPLOAD_ROOT_DIR__ : __TYPECHO_ROOT_DIR__)
215 | . $upload_subdir;
216 |
217 | //创建上传目录
218 | if (!is_dir($path)) {
219 | if (!self::makeUploadDir($path)) {
220 | return false;
221 | }
222 | }
223 |
224 | //获取文件名
225 | if ($subdir_opt == 'origin') {
226 | $fileName = sprintf('%u', crc32(uniqid())) . '.' . $ext;
227 | } else {
228 | $fileName = self::generateFilename() . '.' . $ext;
229 | }
230 | $path = $path . '/' . $fileName;
231 |
232 | $apiKey = Typecho_Widget::widget('Widget_Options')->plugin('MyUpload')->apiKey;
233 | if ($apiKey) {
234 | $client = Typecho_Http_Client::get();
235 | if ($client) {
236 | $client->setHeader('Authorization', 'Basic ' . base64_encode('api:' . $apiKey));
237 | }
238 | }
239 |
240 | if (isset($file['tmp_name'])) {
241 | // 使用远程图片压缩
242 | $remoteCompress = false;
243 | if (isset($client) && $client) {
244 | // 读取图片
245 | $fileHandler = fopen($file['tmp_name'], 'rb');
246 | $fileBytes = fread($fileHandler, filesize($file['tmp_name']));
247 | fclose($fileHandler);
248 | // 执行远程压缩
249 | try {
250 | $picBytes = self::remoteCompressImage($fileBytes, $client);
251 | } catch (Typecho_Http_Client_Exception $exception) {}
252 |
253 | $remoteCompress = isset($picBytes) && $picBytes && file_put_contents($path, $picBytes);
254 | }
255 |
256 | // 如果远程压缩失败,放弃压缩
257 | if (!$remoteCompress) {
258 | //移动上传文件
259 | if (!@move_uploaded_file($file['tmp_name'], $path)) {
260 | return false;
261 | }
262 | }
263 | } else if (isset($file['bytes'])) {
264 | // 似乎不会到这里
265 |
266 | // 使用远程图片压缩
267 | $remoteCompress = false;
268 | if (isset($client) && $client) {
269 | // 执行远程压缩
270 | try {
271 | $picBytes = self::remoteCompressImage($file['bytes'], $client);
272 | } catch (Typecho_Http_Client_Exception $exception) {}
273 |
274 | $remoteCompress = isset($picBytes) && $picBytes && file_put_contents($path, $picBytes);
275 | }
276 |
277 | // 如果远程压缩失败,放弃压缩
278 | if (!$remoteCompress) {
279 | //直接写入文件
280 | if (!file_put_contents($path, $file['bytes'])) {
281 | return false;
282 | }
283 | }
284 | } else {
285 | return false;
286 | }
287 |
288 | // 如果没有填写 apiKey,采取本地命令行压缩
289 | if (empty($apiKey)) {
290 | self::compressImage($path);
291 | }
292 | $ext = strtolower(pathinfo($path)['extension']);
293 | $file['size'] = filesize($path);
294 |
295 | //返回相对存储路径
296 | return array(
297 | 'name' => $file['name'],
298 | 'path' => (defined('__TYPECHO_UPLOAD_DIR__') ? __TYPECHO_UPLOAD_DIR__ : self::UPLOAD_DIR)
299 | . $upload_subdir . '/' . $fileName,
300 | 'size' => $file['size'],
301 | 'type' => $ext,
302 | 'mime' => Typecho_Common::mimeContentType($path)
303 | );
304 | }
305 |
306 | /**
307 | * 修改文件处理函数,如果需要实现自己的文件哈希或者特殊的文件系统,请在options表里把modifyHandle改成自己的函数
308 | *
309 | * @access public
310 | * @param array $content 老文件
311 | * @param array $file 新上传的文件
312 | * @return mixed
313 | * @throws Exception
314 | */
315 | public static function modifyHandle($content, $file)
316 | {
317 | $ext = self::getSafeName($file['name']);
318 |
319 | if ($content['attachment']->type != $ext || Typecho_Common::isAppEngine()) {
320 | return false;
321 | }
322 |
323 | $path = Typecho_Common::url($content['attachment']->path,
324 | defined('__TYPECHO_UPLOAD_ROOT_DIR__') ? __TYPECHO_UPLOAD_ROOT_DIR__ : __TYPECHO_ROOT_DIR__);
325 | $dir = dirname($path);
326 |
327 | //创建上传目录
328 | if (!is_dir($dir)) {
329 | if (!self::makeUploadDir($dir)) {
330 | return false;
331 | }
332 | }
333 |
334 | $apiKey = Typecho_Widget::widget('Widget_Options')->plugin('MyUpload')->apiKey;
335 | if ($apiKey) {
336 | $client = Typecho_Http_Client::get();
337 | if ($client) {
338 | $client->setHeader('Authorization', 'Basic ' . base64_encode('api:' . $apiKey));
339 | }
340 | }
341 |
342 | if (isset($file['tmp_name'])) {
343 |
344 | @unlink($path);
345 |
346 | // 使用远程图片压缩
347 | $remoteCompress = false;
348 | if (isset($client) && $client) {
349 | // 读取图片
350 | $fileHandler = fopen($file['tmp_name'], 'rb');
351 | $fileBytes = fread($fileHandler, filesize($file['tmp_name']));
352 | fclose($fileHandler);
353 | // 执行远程压缩
354 | try {
355 | $picBytes = self::remoteCompressImage($fileBytes, $client);
356 | } catch (Typecho_Http_Client_Exception $exception) {}
357 |
358 | $remoteCompress = isset($picBytes) && $picBytes && file_put_contents($path, $picBytes);
359 | }
360 |
361 | // 如果远程压缩失败,放弃压缩
362 | if (!$remoteCompress) {
363 | //移动上传文件
364 | if (!@move_uploaded_file($file['tmp_name'], $path)) {
365 | return false;
366 | }
367 | }
368 | } else if (isset($file['bytes'])) {
369 |
370 | @unlink($path);
371 |
372 | // 使用远程图片压缩
373 | $remoteCompress = false;
374 | if (isset($client) && $client) {
375 | // 执行远程压缩
376 | try {
377 | $picBytes = self::remoteCompressImage($file['bytes'], $client);
378 | } catch (Typecho_Http_Client_Exception $exception) {}
379 |
380 | $remoteCompress = isset($picBytes) && $picBytes && file_put_contents($path, $picBytes);
381 | }
382 |
383 | // 如果远程压缩失败,放弃压缩
384 | if (!$remoteCompress) {
385 | //直接写入文件
386 | if (!file_put_contents($path, $file['bytes'])) {
387 | return false;
388 | }
389 | }
390 | } else {
391 | return false;
392 | }
393 |
394 | // 如果没有填写 apiKey,采取本地命令行压缩
395 | if (empty($apiKey)) {
396 | self::compressImage($path);
397 | }
398 | $file['size'] = filesize($path);
399 |
400 | //返回相对存储路径
401 | return array(
402 | 'name' => $content['attachment']->name,
403 | 'path' => $content['attachment']->path,
404 | 'size' => $file['size'],
405 | 'type' => $content['attachment']->type,
406 | 'mime' => $content['attachment']->mime
407 | );
408 | }
409 | }
410 |
--------------------------------------------------------------------------------