├── 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 | 如果使用云主机,须安装 jpegoptimpngquant,不要填 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 | --------------------------------------------------------------------------------