uploadHandle = array('AliOssForTypecho_Plugin', 'uploadHandle'); Typecho_Plugin::factory('Widget_Upload')->modifyHandle = array('AliOssForTypecho_Plugin', 'modifyHandle'); Typecho_Plugin::factory('Widget_Upload')->deleteHandle = array('AliOssForTypecho_Plugin', 'deleteHandle'); Typecho_Plugin::factory('Widget_Upload')->attachmentHandle = array('AliOssForTypecho_Plugin', 'attachmentHandle'); Typecho_Plugin::factory('Widget_Upload')->attachmentDataHandle = array('AliOssForTypecho_Plugin', 'attachmentDataHandle'); return _t('启用成功,请进行相应设置!'); } /** * 禁用插件方法,如果禁用失败,直接抛出异常 * * @static * @access public * @return void * @throws Typecho_Plugin_Exception */ public static function deactivate(){} /** * 获取插件配置面板 * * @access public * @param Typecho_Widget_Helper_Form $form 配置面板 * @return void */ public static function config(Typecho_Widget_Helper_Form $form) { $upload_root = Typecho_Common::url(defined('__TYPECHO_UPLOAD_DIR__') ? __TYPECHO_UPLOAD_DIR__ : self::UPLOAD_DIR, defined('__TYPECHO_UPLOAD_ROOT_DIR__') ? __TYPECHO_UPLOAD_ROOT_DIR__ : __TYPECHO_ROOT_DIR__); $log_name = $upload_root . self::LOG_SUFFIX . 'error.log'; if (is_writable($upload_root) && is_writable($upload_root . self::LOG_SUFFIX) && is_writable($log_name)) { $log_content = '恭喜!暂无错误日志产生,请继续保持维护,加油~'; $log_color = '#009900'; if (file_exists($log_name)) { $handle = fopen($log_name, "r"); $content = fread($handle, filesize($log_name)); if ($content) { $log_content = $content; $log_color = '#dd0000'; } fclose($handle); } } else { $log_content = '!!!注意!!! 当前网站上传目录无写入权限,无法记录日志! 请给路径 '.$upload_root.' 赋予写入权限'; $log_color = '#f00000'; } $des = new Typecho_Widget_Helper_Form_Element_Text('des', NULL, '', _t('插件使用说明'), '
  1. 插件更新于2020年6月25号。
  2. 插件基于aliyun-oss-php-sdk Release 2.3.1开发, 若以后SDK开发包更新导致插件不可用,请到 我的博客^-^获取新版本插件,如果我还用typecho、阿里云OSS就会更新。
  3. 若开启“在服务器保留备份”功能,请注意:
    1)请赋予以下目录写权限:'.$upload_root.'
    2)当文件成功上传到OSS,但保存到服务器失败时,插件不会报错,这将导致当前文件在服务器上没有备份,但是会在此插件页面显示日志,请定期查阅并清理。
  4. 3)运行在云应用引擎上的站点“在服务器保留备份”选项无效。
  5. 旧版本Typecho存在无法上传大写扩展名文件的bug,请更新Typecho程序。
  6. 如有问题或建议请到 我的博客https://www.droomo.top/AliOssForTypecho.html 留言

以下是本插件产生的错误日志,请定期查看并处理:

'); $form->addInput($des); $buketName = new Typecho_Widget_Helper_Form_Element_Text('bucketName', NULL, null, _t('Bucket名称'), _t('请填写Buket名称')); $form->addInput($buketName->addRule('required', _t('必须填写Bucket名称'))); $accessKeyId = new Typecho_Widget_Helper_Form_Element_Text('accessKeyId', NULL, null, _t('ACCESS KEY ID'), _t('请填写ACCESS KEY ID')); $form->addInput($accessKeyId->addRule('required', _t('必须填写ACCESS KEY ID'))); $accessKeySecret = new Typecho_Widget_Helper_Form_Element_Text('accessKeySecret', NULL, null, _t('ACCESS KEY SECRET'), _t('请填写请填写ACCESS KEY SECRET')); $form->addInput($accessKeySecret->addRule('required', _t('必须填写ACCESS_KEY'))); $endPoint = new Typecho_Widget_Helper_Form_Element_Select('endPoint', array( "oss-cn-hangzhou" => "华东1(杭州)oss-cn-hangzhou", "oss-cn-shanghai" => "华东2(上海)oss-cn-shanghai", "oss-cn-qingdao" => "华北1(青岛)oss-cn-qingdao", "oss-cn-beijing" => "华北2(北京)oss-cn-beijing", "oss-cn-zhangjiakou" => "华北3(张家口)oss-cn-zhangjiakou", "oss-cn-huhehaote" => "华北5(呼和浩特)oss-cn-huhehaote", "oss-cn-wulanchabu" => "华北6(乌兰察布)oss-cn-wulanchabu", "oss-cn-shenzhen" => "华南1(深圳)oss-cn-shenzhen", "oss-cn-heyuan" => "华南2(河源)oss-cn-heyuan", "oss-cn-chengdu" => "西南1(成都)oss-cn-chengdu", "oss-cn-hongkong" => "中国(香港)oss-cn-hongkong", "oss-us-west-1" => "美国西部1(硅谷)oss-us-west-1", "oss-us-east-1" => "美国东部1(弗吉尼亚)oss-us-east-1", "oss-ap-southeast-1" =>"亚太东南1(新加坡)oss-ap-southeast-1", "oss-ap-southeast-2" =>"亚太东南2(悉尼)oss-ap-southeast-2", "oss-ap-southeast-3" =>"亚太东南3(吉隆坡)oss-ap-southeast-3", "oss-ap-southeast-5" =>"亚太东南5(雅加达)oss-ap-southeast-5", "oss-ap-northeast-1" =>"亚太东北1(日本)oss-ap-northeast-1", "oss-ap-south-1" => "亚太南部1(孟买)oss-ap-south-1", "oss-eu-central-1" => "欧洲中部1(法兰克福)oss-eu-central-1", "oss-eu-west-1" => "英国(伦敦)oss-eu-west-1", "oss-me-east-1" => "中东东部1(迪拜)oss-me-east-1", "other" => '自定义' ), 'oss-cn-qingdao', _t('区域选择(若区域不在列表中则选择自定义,然后填写区域)'), ''); $form->addInput($endPoint); $endPointType = new Typecho_Widget_Helper_Form_Element_Select('endPointType', array( ".aliyuncs.com" => '外网', "-internal.aliyuncs.com" => '内网', ), '.aliyuncs.com', '', '在你了解两种连接方式的不同作用的情况下修改此选项'); $form->addInput($endPointType); $otherEndPoint = new Typecho_Widget_Helper_Form_Element_Text('otherEndPoint', NULL, '', '', ' 填写全部Endpoint地址,通常以\'.aliyuncs.com\'或\'-internal.aliyuncs.com\'结尾。开头不包含http://,结尾不包含"/"。
例如"oss-cn-qingdao.aliyuncs.com"
'); $form->addInput($otherEndPoint); $userDir = new Typecho_Widget_Helper_Form_Element_Text('userDir', NULL, 'typecho/', _t('要储存的路径'), _t('请填写文件储存的路径(相对OSS根目录),以字母或数字开头,以"/"结尾。留空则上传到根目录。')); $form->addInput($userDir); $cdnUrl = new Typecho_Widget_Helper_Form_Element_Text('cdnUrl', NULL, '', _t('自定义(CDN)域名'), '请填写自定义域名,留空则使用外网Endpoint访问,以http://或https://开头,以"/"结尾'); $form->addInput($cdnUrl); $ifLoaclSave = new Typecho_Widget_Helper_Form_Element_Radio('ifLoaclSave', array( "1" => '保留', "0" => '不保留' ), "1", _t('在服务器保留备份'), _t('是否在服务器保留备份')); $form->addInput($ifLoaclSave); echo ''; } /** * 个人用户的配置面板 * * @access public * @param Typecho_Widget_Helper_Form $form * @return void */ public static function personalConfig(Typecho_Widget_Helper_Form $form){} /** * 上传文件处理函数 * * @access public * @param array $file 上传的文件 * @return mixed */ public static function uploadHandle($file) { if (empty($file['name'])) { return FALSE; } $ext = self::getExtentionName($file['name']); if (!self::checkFileType($ext)) { return FALSE; } if (isset($file['tmp_name'])) { $content = file_get_contents($file['tmp_name']); } else if (isset($file['bytes'])) { $content = $file['bytes']; } else { return FALSE; } $options = Typecho_Widget::widget('Widget_Options'); $user_dir = $options->plugin('AliOssForTypecho')->userDir; $bucket_name = $options->plugin('AliOssForTypecho')->bucketName; $end_point = 'http://' . (($options->plugin('AliOssForTypecho')->endPoint === "other") ? $options->plugin('AliOssForTypecho')->otherEndPoint : $options->plugin('AliOssForTypecho')->endPoint . $options->plugin('AliOssForTypecho')->endPointType); $access_id = $options->plugin('AliOssForTypecho')->accessKeyId; $access_key = $options->plugin('AliOssForTypecho')->accessKeySecret; try { $oss_client = new OssClient($access_id, $access_key, $end_point); } catch (Exception $e) { return FALSE; } $save_on_server = $options->plugin('AliOssForTypecho')->ifLoaclSave; $file_origin_name = self::getSafeName($file['name']); $relative_path = date('Y/m/d/'); $remote_file_name = $user_dir . $relative_path . $file_origin_name; if ($save_on_server === "1" && !Typecho_Common::isAppEngine()) { $upload_root = Typecho_Common::url(defined('__TYPECHO_UPLOAD_DIR__') ? __TYPECHO_UPLOAD_DIR__ : self::UPLOAD_DIR, defined('__TYPECHO_UPLOAD_ROOT_DIR__') ? __TYPECHO_UPLOAD_ROOT_DIR__ : __TYPECHO_ROOT_DIR__); $local_file_name = $upload_root . $relative_path . $file_origin_name; try{ $exist_on_oss = $oss_client->doesObjectExist($bucket_name, $remote_file_name); $exist_on_server = file_exists($local_file_name); } catch(OssException $e) { return false; } if ($exist_on_oss || $exist_on_server) { // find a name neither exist on oss nor the server $pathinfo = pathinfo($file_origin_name); for ($i = 1;; $i++) { $file_origin_name = $pathinfo['filename'] . '(' . strval($i) . ').' . self::getExtentionName($file_origin_name); $remote_file_name = $user_dir . $relative_path . $file_origin_name; $local_file_name = $upload_root . $relative_path . $file_origin_name; try{ $exist_on_oss = $oss_client->doesObjectExist($bucket_name, $remote_file_name); $exist_on_server = file_exists($local_file_name); } catch(OssException $e) { return false; } if ($exist_on_oss || $exist_on_server) { } else { break; } } } } else { try{ $exist_on_oss = $oss_client->doesObjectExist($bucket_name, $remote_file_name); } catch(OssException $e) { return false; } if ($exist_on_oss || $exist_on_server) { // find a name not exist on oss $pathinfo = pathinfo($file_origin_name); for ($i = 1;; $i++) { $file_origin_name = $pathinfo['filename'] . '(' . strval($i) . ').' . self::getExtentionName($file_origin_name); $remote_file_name = $user_dir . $relative_path . $file_origin_name; try{ $exist_on_oss = $oss_client->doesObjectExist($bucket_name, $remote_file_name); } catch(OssException $e) { return false; } if (!$exist_on_oss) { break; } } } } try{ $ali_response = $oss_client->putObject($bucket_name, $remote_file_name, $content); } catch(OssException $e) { return false; } if (200 != $ali_response['info']['http_code']) { return FALSE; } else { $object_url = $ali_response['info']["url"]; if ($save_on_server === "1" && !Typecho_Common::isAppEngine()) { $file_dir_name = dirname($local_file_name); $dir_exist = false; if (is_dir($file_dir_name)) { $dir_exist = true; } else { if (self::makeUploadDir($file_dir_name)) { $dir_exist = true; } } $error_log_path = $upload_root . self::LOG_SUFFIX; if ($dir_exist) { if (!file_put_contents($local_file_name, $content)) { return false; } } else { return false; $error = '错误:创建目录失败' . "\r\n" . '创建路径:' . $localPath . "\r\n" . '远程文件:' . $object_url . "\r\n" . '时间:' . date('Y-m-d h:i:sa') . "\r\n\r\n"; error_log($error, 3, $error_log_path . "error.log"); } } return array( 'name' => $file_origin_name, 'path' => $remote_file_name, 'size' => intval($ali_response['oss-requestheaders']['Content-Length']), 'type' => $ext, 'mime' => $ali_response['oss-requestheaders']['Content-Type'] ); } } /** * 修改文件处理函数 * * @access public * @param array $content 老文件 * @param array $file 新上传的文件 * @return mixed */ public static function modifyHandle($content, $file) { if (empty($file['name'])) { return false; } $options = Typecho_Widget::widget('Widget_Options'); $userDir = $options->plugin('AliOssForTypecho')->userDir; $bucket_name = $options->plugin('AliOssForTypecho')->bucketName; $end_point = 'http://' . (($options->plugin('AliOssForTypecho')->endPoint === "other") ? $options->plugin('AliOssForTypecho')->otherEndPoint : $options->plugin('AliOssForTypecho')->endPoint . $options->plugin('AliOssForTypecho')->endPointType); $access_id = $options->plugin('AliOssForTypecho')->accessKeyId; $access_key = $options->plugin('AliOssForTypecho')->accessKeySecret; $ext = self::getExtentionName($file['name']); if ($content['attachment']->type != $ext) { return false; } $path = $content['attachment']->path; $object_name = $userDir . $path; if (isset($file['tmp_name'])) { $newContent = file_get_contents($file['tmp_name']); } else if (isset($file['bytes'])) { $newContent = $file['bytes']; } else { return false; } try { $oss_client = new OssClient($access_id, $access_key, $end_point); } catch (Exception $e) { throw new Exception( $e->getMessage()); } $ali_response = $oss_client->putObject($bucket_name, $object_name, $newContent); if (200 != $ali_response['info']['http_code']) { return FALSE; } else { $object_url = $ali_response["info"]["url"]; $ifLoaclSave = $options->plugin('AliOssForTypecho')->ifLoaclSave; if ($ifLoaclSave === "1" && !Typecho_Common::isAppEngine()) { $upload_root = Typecho_Common::url(defined('__TYPECHO_UPLOAD_DIR__') ? __TYPECHO_UPLOAD_DIR__ : self::UPLOAD_DIR, defined('__TYPECHO_UPLOAD_ROOT_DIR__') ? __TYPECHO_UPLOAD_ROOT_DIR__ : __TYPECHO_ROOT_DIR__); $localFile = $upload_root . $path; $localPath = dirname($localFile); $mkdirSuccess = TRUE; if (!is_dir($localPath)) { if (!self::makeUploadDir($localPath)) { $mkdirSuccess = FALSE; } } if ($mkdirSuccess) { $deleteLacalFileSuccess = unlink($localFile); if (!$deleteLacalFileSuccess) { $error_log_path = $upload_root . self::LOG_SUFFIX; if (!is_dir($error_log_path)) { self::makeUploadDir($error_log_path); } $error = '错误:删除文件失败导致无法修改文件' . "\r\n" . '文件:' . $localFile . "\r\n" . '远程文件:' . $object_url . "\r\n" . '时间:' . date('Y-m-d h:i:sa') . "\r\n\r\n"; error_log($error, 3, $error_log_path . "error.log"); } else { if (file_put_contents($localFile, $newContent)) { } else { $error = '错误:保存文件失败' . "\r\n" . '远程文件:' . $object_url . "\r\n" . '时间:' . date('Y-m-d h:i:sa') . "\r\n\r\n"; error_log($error, 3, $error_log_path . "error.log"); } } } else { $error = '错误:创建目录失败' . "\r\n" . '创建路径:' . $localPath . "\r\n" . '远程文件:' . $object_url . "\r\n" . '时间:' . date('Y-m-d h:i:sa') . "\r\n\r\n"; error_log($error, 3, $error_log_path . "error.log"); } } } return array( 'name' => $content['attachment']->name, 'path' => $path, 'size' => intval($ali_response['oss-requestheaders']['Content-Length']), 'type' => $ext, 'mime' => $ali_response['oss-requestheaders']['Content-Type'] ); } /** * 删除文件 * * @access public * @param array $content 文件相关信息 * @return string */ public static function deleteHandle(array $content) { $options = Typecho_Widget::widget('Widget_Options'); $userDir = $options->plugin('AliOssForTypecho')->userDir; $bucket_name = $options->plugin('AliOssForTypecho')->bucketName; $end_point = 'http://' . (($options->plugin('AliOssForTypecho')->endPoint === "other") ? $options->plugin('AliOssForTypecho')->otherEndPoint : $options->plugin('AliOssForTypecho')->endPoint . $options->plugin('AliOssForTypecho')->endPointType); $access_id = $options->plugin('AliOssForTypecho')->accessKeyId; $access_key = $options->plugin('AliOssForTypecho')->accessKeySecret; try { $oss_client = new OssClient($access_id, $access_key, $end_point); } catch (Exception $e) { throw new Exception( $e->getMessage()); } $path = $content['attachment']->path; $object_name = $userDir . $path; $ali_response = $oss_client->deleteObject($bucket_name, $object_name); $ifLoaclSave = $options->plugin('AliOssForTypecho')->ifLoaclSave; if ($ifLoaclSave === "1" && !Typecho_Common::isAppEngine()) { $upload_root = Typecho_Common::url(defined('__TYPECHO_UPLOAD_DIR__') ? __TYPECHO_UPLOAD_DIR__ : self::UPLOAD_DIR, defined('__TYPECHO_UPLOAD_ROOT_DIR__') ? __TYPECHO_UPLOAD_ROOT_DIR__ : __TYPECHO_ROOT_DIR__); $localPath = $upload_root . $path; $deleteLacalFileSuccess = unlink($localPath); if (!$deleteLacalFileSuccess) { $error_log_path = $localPath . self::LOG_SUFFIX; if (!is_dir($error_log_path)) { self::makeUploadDir($error_log_path); } $error = '错误:删除文件失败' . "\r\n" . '文件:' . $localPath . "\r\n" . '时间:' . date('Y-m-d h:i:sa') . "\r\n\r\n"; error_log($error, 3, $error_log_path . "error.log"); } } return ($ali_response['info']['http_code'] === 204); } /** * 获取实际文件绝对访问路径 * * @access public * @param array $content 文件相关信息 * @return string */ public static function attachmentHandle(array $content) { $options = Typecho_Widget::widget('Widget_Options'); $cdnUrl = $options->plugin('AliOssForTypecho')->cdnUrl; if ($cdnUrl == '') { $bucket_name = $options->plugin('AliOssForTypecho')->bucketName; $end_point = ($options->plugin('AliOssForTypecho')->endPoint === "other") ? $options->plugin('AliOssForTypecho')->otherEndPoint : $options->plugin('AliOssForTypecho')->endPoint; return 'https://' . $bucket_name . '.' . $end_point . '.aliyuncs.com/' . $content['attachment']->path; } else { return $cdnUrl . $content['attachment']->path; } } /** * 获取实际文件数据 * * @access public * @param array $content * @return string */ public static function attachmentDataHandle(array $content) { return file_get_contents(self::attachmentHandle($content)); } /** * 检查文件名 * * @access private * @param string $ext 扩展名 * @return boolean */ private static function checkFileType($ext) { $options = Typecho_Widget::widget('Widget_Options'); return in_array($ext, $options->allowedAttachmentTypes); } /** * 创建上传路径 * * @access private * @param string $path 路径 * @return boolean */ private static function makeUploadDir($path) { $path = preg_replace("/\\\+/", '/', $path); $current = rtrim($path, '/'); $last = $current; while (!is_dir($current) && false !== strpos($path, '/')) { $last = $current; $current = dirname($current); } if ($last == $current) { return true; } if (!@mkdir($last)) { return false; } $stat = @stat($last); $perms = $stat['mode'] & 0007777; @chmod($last, $perms); return self::makeUploadDir($path); } /** * 获取安全的文件名 * * @param string $name * @static * @access private * @return string */ private static function getSafeName(&$name) { $name = str_replace(array('"', '<', '>'), '', $name); $name = str_replace('\\', '/', $name); $name = false === strpos($name, '/') ? ('a' . $name) : str_replace('/', '/a', $name); $info = pathinfo($name); $name = substr($info['basename'], 1); return $name; } private static function getExtentionName(&$name) { $info = pathinfo($name); return isset($info['extension']) ? strtolower($info['extension']) : ''; } }