├── .gitignore ├── README.md ├── composer.json ├── src ├── AliOss.php ├── Ftp.php ├── Local.php ├── MyHttp.php ├── MyHttpTrait.php ├── Qiniu.php └── StorageInterface.php └── test ├── ftp.php ├── jquery-file-upload ├── cors │ ├── postmessage.html │ └── result.html ├── css │ ├── demo-ie8.css │ ├── demo.css │ ├── jquery.fileupload-noscript.css │ ├── jquery.fileupload-ui-noscript.css │ ├── jquery.fileupload-ui.css │ ├── jquery.fileupload.css │ └── style.css ├── img │ ├── loading.gif │ └── progressbar.gif └── js │ ├── app.js │ ├── cors │ ├── jquery.postmessage-transport.js │ └── jquery.xdr-transport.js │ ├── jquery.fileupload-angular.js │ ├── jquery.fileupload-audio.js │ ├── jquery.fileupload-image.js │ ├── jquery.fileupload-jquery-ui.js │ ├── jquery.fileupload-process.js │ ├── jquery.fileupload-ui.js │ ├── jquery.fileupload-validate.js │ ├── jquery.fileupload-video.js │ ├── jquery.fileupload.js │ ├── jquery.iframe-transport.js │ ├── main.js │ └── vendor │ └── jquery.ui.widget.js ├── oss.php ├── qiniu-form.php └── qiniu.php /.gitignore: -------------------------------------------------------------------------------- 1 | Thumbs.db 2 | .DS_Store 3 | .sass-cache 4 | .idea 5 | /vendor 6 | /composer.lock 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 文件存储 2 | 3 | > 安装 4 | 5 | ``` 6 | composer require pfinal/storage 7 | ``` 8 | 9 | * 本地存储 Local 10 | 11 | * 阿里云 AliOss 12 | 13 | 请先 composer require aliyuncs/oss-sdk-php 14 | 15 | * 7牛存诸 Qiniu 16 | 17 | 请先 composer require qiniu/php-sdk:7.1.* 18 | 19 | * Ftp 20 | 21 | 请先 composer require league/flysystem 22 | 23 | 24 | > 提供接口 25 | 26 | ``` 27 | //上传文件 28 | public function put($key, $data); 29 | //获取url 30 | public function url($key, $rule = null); 31 | //重命名 32 | public function rename($key, $newKey); 33 | //删除 34 | public function delete($key); 35 | //获取错误消息 36 | public function error(); 37 | ``` 38 | 39 | > 使用示例 40 | 41 | ```php 42 | 'TOeV-fwwxsssf3s_45tCziKjRD9-bPyXUKjbuX7b', 49 | 'secretKey' => 'pbHrgwwwp_wpClxeeGrYKLNdEhLd02Jrew3t5h', 50 | 'bucketName' => 'test', 51 | 'baseUrl' => 'http://static.pfinal.cn/', 52 | 'separator' => '-', 53 | ); 54 | $qiniu = new \PFinal\Storage\Qiniu($config); 55 | $bool = $qiniu->put('test/1.jpg', file_get_contents('/Users/ethan/Pictures/1.jpg')); 56 | 57 | //原图url 58 | var_dump($qiniu->url('test/1.jpg')); 59 | 60 | //小图url 规则: "m" 61 | var_dump($qiniu->url('test/1.jpg', 'm')); 62 | 63 | 64 | //阿里云OSS 65 | 66 | $config = array( 67 | 'accessKey' => 'your key', 68 | 'secret' => 'your secret', 69 | 'endPoint' => 'oss-cn-shanghai.aliyuncs.com', 70 | 'bucket' => 'your bucket', 71 | ); 72 | $oss = new \PFinal\Storage\AliOss($config); 73 | 74 | $bool = $oss->put('test.jpg', file_get_contents('/Users/ethan/Pictures/1.jpg')); 75 | 76 | //原图url 77 | echo $oss->url('test.jpg'); 78 | 79 | //小图url 规则名称: "s" 80 | echo $oss->url('test.jpg', 's'); 81 | 82 | ``` 83 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pfinal/storage", 3 | "description": "storage", 4 | "license": "MIT", 5 | "homepage": "http://www.pfinal.cn", 6 | "support": { 7 | "source": "https://github.com/pfinal/storage" 8 | }, 9 | "authors": [ 10 | { 11 | "name": "Zou Yiliang" 12 | } 13 | ], 14 | "require": { 15 | "php": ">=5.3" 16 | }, 17 | "autoload": { 18 | "psr-4": { 19 | "PFinal\\Storage\\": "src/" 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/AliOss.php: -------------------------------------------------------------------------------- 1 | $item) { 28 | $this->$key = $item; 29 | } 30 | } 31 | 32 | /** 33 | * 上传文件 34 | * 35 | * @param string $key 36 | * @param mixed $data 数据 例如 $data = file_get_contents('/Users/ethan/Pictures/1.jpg'); 37 | * @return bool 38 | */ 39 | public function put($key, $data) 40 | { 41 | //初始化阿里oss 42 | $ossClient = new OssClient($this->accessKey, $this->secret, $this->endPoint); 43 | 44 | //上传 45 | try { 46 | $ossClient->putObject($this->bucket, $key, $data); 47 | return true; 48 | } catch (OssException $e) { 49 | $this->error = $e->getMessage(); 50 | return false; 51 | } 52 | } 53 | 54 | 55 | /** 56 | * 删除单个文件 57 | * 58 | * @param string $key 59 | * @return bool 60 | */ 61 | public function delete($key) 62 | { 63 | $ossClient = new OssClient($this->accessKey, $this->secret, $this->endPoint); 64 | 65 | try { 66 | $ossClient->deleteObject($this->bucket, $key); 67 | } catch (OssException $e) { 68 | $this->error = $e->getMessage(); 69 | return false; 70 | } 71 | 72 | return true; 73 | } 74 | 75 | /** 76 | * 返回文件外链 77 | * 78 | * @param $key 79 | * @param string|null $rule 处理规则,为null时原样返回 80 | * @return string 81 | */ 82 | public function url($key, $rule = null) 83 | { 84 | $append = ''; 85 | if ($rule != null) { 86 | //http://kushu-test.oss-cn-shanghai.aliyuncs.com/haha.jpg?x-oss-process=style/s 87 | $append = $this->separator . $rule; 88 | } 89 | 90 | //$scheme = isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) !== 'off' ? 'https' : 'http'; 91 | if (isset($_SERVER['HTTP_X_FORWARDED_PROTO'])) { 92 | $scheme = strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']); 93 | } else { 94 | $scheme = isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) !== 'off' ? 'https' : 'http'; 95 | } 96 | 97 | if ($this->cdn != null) { 98 | return $scheme . '://' . $this->cdn . '/' . $key . $append; 99 | } 100 | 101 | return $scheme . '://' . $this->bucket . '.' . $this->endPoint . '/' . $key . $append; 102 | } 103 | 104 | /** 105 | * 重命名文件 106 | * 107 | * @param $key 108 | * @param $newKey 109 | * @return bool 110 | */ 111 | public function rename($key, $newKey) 112 | { 113 | $ossClient = new OssClient($this->accessKey, $this->secret, $this->endPoint); 114 | 115 | try { 116 | 117 | //todo 没看到有重命名名接口,暂时先这样处理 118 | 119 | $ossClient->copyObject($this->bucket, $key, $this->bucket, $newKey); 120 | $ossClient->deleteObject($this->bucket, $key); 121 | } catch (OssException $e) { 122 | $this->error = $e->getMessage(); 123 | return false; 124 | } 125 | 126 | return true; 127 | } 128 | 129 | /** 130 | * 复制文件 131 | * 132 | * @param $key 133 | * @param $newKey 134 | * @return bool 135 | */ 136 | public function copy($key, $newKey) 137 | { 138 | $ossClient = new OssClient($this->accessKey, $this->secret, $this->endPoint); 139 | 140 | try { 141 | $ossClient->copyObject($this->bucket, $key, $this->bucket, $newKey); 142 | } catch (OssException $e) { 143 | $this->error = $e->getMessage(); 144 | return false; 145 | } 146 | 147 | return true; 148 | } 149 | 150 | 151 | /** 152 | * 错误消息 153 | * 154 | * @return string 155 | */ 156 | public function error() 157 | { 158 | return $this->error; 159 | } 160 | 161 | 162 | // $callbackUrl测试时,未收到aliyun的回调 163 | // https://help.aliyun.com/document_detail/31988.html 164 | public function getClientToken($dir = 'uploads/', $callbackUrl = '') 165 | { 166 | $id = $this->accessKey; 167 | $key = $this->secret; 168 | // $host的格式为 bucketname.endpoint,请替换为您的真实信息。 169 | $host = '//' . $this->bucket . '.' . $this->endPoint;// 'http://bucket-name.oss-cn-hangzhou.aliyuncs.com'; 170 | // $callbackUrl为上传回调服务器的URL,请将下面的IP和Port配置为您自己的真实URL信息。 171 | // $callbackUrl = 'http://88.88.88.88:8888/aliyun-oss-appserver-php/php/callback.php'; 172 | // $dir = 'user-dir-prefix/'; // 用户上传文件时指定的前缀。 173 | 174 | $callback_param = array('callbackUrl' => $callbackUrl, 175 | 'callbackBody' => 'filename=${object}&size=${size}&mimeType=${mimeType}&height=${imageInfo.height}&width=${imageInfo.width}', 176 | 'callbackBodyType' => "application/x-www-form-urlencoded"); 177 | $callback_string = json_encode($callback_param); 178 | 179 | $base64_callback_body = base64_encode($callback_string); 180 | $now = time(); 181 | $expire = 30; //设置该policy超时时间是10s. 即这个policy过了这个有效时间,将不能访问。 182 | $end = $now + $expire; 183 | $expiration = self::gmt_iso8601($end); 184 | 185 | //最大文件大小.用户可以自己设置 186 | $condition = array(0 => 'content-length-range', 1 => 0, 2 => 1048576000); 187 | $conditions[] = $condition; 188 | 189 | // 表示用户上传的数据,必须是以$dir开始,不然上传会失败,这一步不是必须项,只是为了安全起见,防止用户通过policy上传到别人的目录。 190 | $start = array(0 => 'starts-with', 1 => '$key', 2 => $dir); 191 | $conditions[] = $start; 192 | 193 | 194 | $arr = array('expiration' => $expiration, 'conditions' => $conditions); 195 | $policy = json_encode($arr); 196 | $base64_policy = base64_encode($policy); 197 | $string_to_sign = $base64_policy; 198 | $signature = base64_encode(hash_hmac('sha1', $string_to_sign, $key, true)); 199 | 200 | $response = array(); 201 | $response['accessId'] = $id; 202 | $response['host'] = $host; 203 | $response['policy'] = $base64_policy; 204 | $response['signature'] = $signature; 205 | $response['expire'] = $end; 206 | $response['callback'] = $base64_callback_body; 207 | $response['dir'] = $dir; // 这个参数是设置用户上传文件时指定的前缀。 208 | return $response; 209 | } 210 | 211 | public function gmt_iso8601($time) 212 | { 213 | $dtStr = date("c", $time); 214 | $mydatetime = new \DateTime($dtStr); 215 | $expiration = $mydatetime->format(\DateTime::ISO8601); 216 | $pos = strpos($expiration, '+'); 217 | $expiration = substr($expiration, 0, $pos); 218 | return $expiration . "Z"; 219 | } 220 | 221 | } -------------------------------------------------------------------------------- /src/Ftp.php: -------------------------------------------------------------------------------- 1 | $item) { 23 | $this->$key = $item; 24 | } 25 | } 26 | 27 | /** 28 | * 上传 29 | * 30 | * @param string $key 31 | * @param $data 32 | * @return bool 33 | */ 34 | public function put($key, $data) 35 | { 36 | $config = array( 37 | 'host' => $this->host, 38 | 'username' => $this->username, 39 | 'password' => $this->password, 40 | 41 | 'port' => $this->port, 42 | 'passive' => $this->passive, 43 | 'timeout' => $this->timeout, 44 | ); 45 | $adapter = new BaseFtp($config); 46 | $fs = new Filesystem($adapter); 47 | 48 | $bool = @$fs->put($key, $data); 49 | return $bool; 50 | } 51 | 52 | /** 53 | * 返回文件外链 54 | * 55 | * @param $key 56 | * @param string|null $rule 处理规则,为null时原样返回 57 | * @return string 58 | */ 59 | public function url($key, $rule = null) 60 | { 61 | //todo 支持rule 62 | 63 | return $this->baseUrl . $key; 64 | } 65 | 66 | /** 67 | * 移动文件 68 | * 69 | * @param $key 70 | * @param $newKey 71 | * @return bool 72 | */ 73 | public function rename($key, $newKey) 74 | { 75 | $config = array( 76 | 'host' => $this->host, 77 | 'username' => $this->username, 78 | 'password' => $this->password, 79 | 80 | 'port' => $this->port, 81 | 'passive' => $this->passive, 82 | 'timeout' => $this->timeout, 83 | ); 84 | $adapter = new BaseFtp($config); 85 | $fs = new Filesystem($adapter); 86 | 87 | $bool = @$fs->rename($key, $newKey); 88 | return $bool; 89 | } 90 | 91 | /** 92 | * 复制文件 93 | * 94 | * @param $key 95 | * @param $newKey 96 | * @return bool 97 | */ 98 | public function copy($key, $newKey) 99 | { 100 | $config = array( 101 | 'host' => $this->host, 102 | 'username' => $this->username, 103 | 'password' => $this->password, 104 | 105 | 'port' => $this->port, 106 | 'passive' => $this->passive, 107 | 'timeout' => $this->timeout, 108 | ); 109 | $adapter = new BaseFtp($config); 110 | $fs = new Filesystem($adapter); 111 | 112 | return @$fs->copy($key, $newKey); 113 | } 114 | 115 | /** 116 | * 删除文件 117 | * 118 | * @param $key 119 | * @return bool 120 | */ 121 | public function delete($key) 122 | { 123 | $config = array( 124 | 'host' => $this->host, 125 | 'username' => $this->username, 126 | 'password' => $this->password, 127 | 128 | 'port' => $this->port, 129 | 'passive' => $this->passive, 130 | 'timeout' => $this->timeout, 131 | ); 132 | $adapter = new BaseFtp($config); 133 | $fs = new Filesystem($adapter); 134 | 135 | $bool = @$fs->delete($key); 136 | return $bool; 137 | } 138 | 139 | /** 140 | * 错误消息 141 | * 142 | * @return string 143 | */ 144 | public function error() 145 | { 146 | // TODO: Implement error() method. 147 | } 148 | } -------------------------------------------------------------------------------- /src/Local.php: -------------------------------------------------------------------------------- 1 | $item) { 13 | $this->$key = $item; 14 | } 15 | } 16 | 17 | /** 18 | * 保存文件 19 | * 20 | * @param string $key 21 | * @param $data 22 | * @return bool 23 | */ 24 | public function put($key, $data) 25 | { 26 | $filename = $this->getFullName($key); 27 | if (!file_exists(dirname($filename))) { 28 | mkdir(dirname($filename), 0777, true); 29 | } 30 | $size = file_put_contents($filename, $data, LOCK_EX); 31 | return $size !== false; 32 | } 33 | 34 | /** 35 | * 返回文件外链 36 | * @param $key 37 | * @param string|null $rule 38 | * @return string 39 | */ 40 | public function url($key, $rule = null) 41 | { 42 | if ($rule != null) { 43 | $key = $this->getThumbKey($key, $rule); 44 | } 45 | 46 | return $this->baseUrl . $key; 47 | } 48 | 49 | /** 50 | * 重命名 51 | * 52 | * @param $key 53 | * @param $newKey 54 | * @return bool 55 | */ 56 | public function rename($key, $newKey) 57 | { 58 | $newFile = $this->getFullName($newKey); 59 | if (!file_exists(dirname($newFile))) { 60 | mkdir(dirname($newFile), 0777, true); 61 | } 62 | 63 | return rename($this->getFullName($key), $newFile); 64 | } 65 | 66 | /** 67 | * 复制 68 | * 69 | * @param $key 70 | * @param $newKey 71 | * @return bool 72 | */ 73 | public function copy($key, $newKey) 74 | { 75 | $newFile = $this->getFullName($newKey); 76 | if (!file_exists(dirname($newFile))) { 77 | mkdir(dirname($newFile), 0777, true); 78 | } 79 | 80 | return copy($this->getFullName($key), $newFile); 81 | } 82 | 83 | /** 84 | * 完整文件名 85 | * @param $key 86 | * @return string 87 | */ 88 | protected function getFullName($key) 89 | { 90 | //todo 验证不能到basePath的上级 91 | 92 | return rtrim($this->basePath, '/\\') . DIRECTORY_SEPARATOR . $key; 93 | } 94 | 95 | /** 96 | * 删除文件 97 | * 98 | * @param $key 99 | * @return bool 100 | */ 101 | public function delete($key) 102 | { 103 | if (!file_exists($this->getFullName($key))) { 104 | return true; 105 | } 106 | return unlink($this->getFullName($key)); 107 | } 108 | 109 | /** 110 | * 错误消息 111 | * 112 | * @return string 113 | */ 114 | public function error() 115 | { 116 | // TODO: Implement error() method. 117 | } 118 | 119 | /** 120 | * @param string $key "uploads/201809/02/xxx.jpg" 121 | * @param string $rule "s" 122 | * @return string "uploads/thumb/s/201809/02/xxx.jpg" 123 | */ 124 | public function getThumbKey($key, $rule) 125 | { 126 | return preg_replace('/^uploads\//', 'uploads/thumb/' . $rule . '/', $key); 127 | } 128 | 129 | /** 130 | * 生成缩略图文件 131 | * @param string $key 最前面不要加斜线 示例: "uploads/201809/02/xxx.jpg" 132 | * @param string $rule 需要生成的规格 示例: "s" 133 | * @param array $allowRules 允许缩略图规格列表 134 | * @return \Leaf\Response|\Symfony\Component\HttpFoundation\StreamedResponse 135 | * 136 | * //访问时动态生成缩略图 137 | * //http://example.com/uploads/201703/07/2.jpg 138 | * //http://example.com/uploads/thumb/m/201703/07/2.jpg 139 | * Route::any('uploads/thumb/:rule/:month/:day/:file.:ext', function (\Leaf\Application $app, $month, $day, $rule, $file, $ext) { 140 | * $fullFileName = 'uploads/' . $month . '/' . $day . '/' . $file . '.' . $ext; 141 | * return $app['storage']->thumb($fullFileName, $rule); 142 | * }); 143 | */ 144 | public function thumb($key, $rule, $allowRules = null) 145 | { 146 | if (empty($allowRules)) { 147 | $allowRules = array( 148 | 's' => array('w' => 250, 'h' => 250, 'cut' => true), 149 | 'm' => array('w' => 400, 'h' => 400,), 150 | 'l' => array('w' => 800, 'h' => 800,), 151 | ); 152 | } 153 | 154 | if (!array_key_exists($rule, $allowRules)) { 155 | return new \Leaf\Response('404 Not Found', 404); 156 | } 157 | 158 | $fullFileName = $this->getFullName($key); 159 | 160 | $thumbnailKey = $this->getThumbKey($key, $rule); 161 | $newFileName = $this->getFullName($thumbnailKey); 162 | 163 | if (!file_exists($fullFileName)) { 164 | return new \Leaf\Response('404 Not Found', 404); 165 | } 166 | 167 | if (isset($allowRules[$rule]['cut']) && $allowRules[$rule]['cut']) { 168 | \Leaf\Image::thumbCut($fullFileName, $newFileName, $allowRules[$rule]['w'], $allowRules[$rule]['h']); 169 | } else { 170 | \Leaf\Image::resize($fullFileName, $newFileName, $allowRules[$rule]['w'], $allowRules[$rule]['h']); 171 | } 172 | 173 | $ext = ltrim(strtolower(strrchr($thumbnailKey, '.')), '.'); 174 | 175 | //$header = array('Content-type' => 'image/png'); 176 | $header = array('Content-type' => 'image/' . $ext); 177 | 178 | return new \Symfony\Component\HttpFoundation\StreamedResponse(function () use ($newFileName) { 179 | readfile($newFileName); 180 | }, 200, $header); 181 | } 182 | 183 | 184 | } -------------------------------------------------------------------------------- /src/MyHttp.php: -------------------------------------------------------------------------------- 1 | $item) { 26 | $this->$key = $item; 27 | } 28 | } 29 | 30 | public function appendSign($data) 31 | { 32 | $timestamp = (string)time(); 33 | $nonce = (string)rand(1000000, 9999999); 34 | $sign = md5($this->accessKey . $timestamp . $nonce . $this->secretKey); 35 | 36 | return array_merge($data, array( 37 | 'accessKey' => $this->accessKey, 38 | 'timestamp' => $timestamp, 39 | 'nonce' => $nonce, 40 | 'sign' => $sign, 41 | )); 42 | } 43 | 44 | /** 45 | * 上传文件 46 | * 47 | * @param string $key 48 | * @param $data 49 | * @return bool 50 | */ 51 | public function put($key, $data) 52 | { 53 | //$tempFile = sys_get_temp_dir() . DIRECTORY_SEPARATOR . uniqid(time(), true); 54 | $tempFile = tempnam(sys_get_temp_dir(), uniqid()); 55 | file_put_contents($tempFile, $data); 56 | 57 | $client = new Client(); 58 | $res = $client->file($this->api, 'file', $tempFile, $this->appendSign(array('filename' => $key))); 59 | 60 | unlink($tempFile); 61 | 62 | $this->error = $res->getBody(); 63 | 64 | if ($res->getStatusCode() == 200) { 65 | $arr = @json_decode($res->getBody(), true); 66 | return is_array($arr) && array_key_exists('status', $arr) && $arr['status']; 67 | } 68 | 69 | return false; 70 | } 71 | 72 | /** 73 | * 返回文件外链 74 | * 75 | * @param $key 76 | * @param string|null $rule 处理规则,为null时原样返回 77 | * @return string 78 | */ 79 | public function url($key, $rule = null) 80 | { 81 | if ($rule) { 82 | $key = dirname($key) . '/' . $rule . '/' . basename($key); 83 | } 84 | return $this->baseUrl . $key; 85 | } 86 | 87 | /** 88 | * 移动文件 89 | * 90 | * @param $key 91 | * @param $newKey 92 | * @return bool 93 | */ 94 | public function rename($key, $newKey) 95 | { 96 | $client = new Client(); 97 | 98 | $res = $client->post($this->api, $this->appendSign(array( 99 | 'action' => 'move', 100 | 'key' => $key, 101 | 'newKey' => $newKey, 102 | ))); 103 | 104 | $this->error = $res->getBody(); 105 | 106 | if ($res->getStatusCode() == 200) { 107 | $arr = @json_decode($res->getBody(), true); 108 | return is_array($arr) && array_key_exists('status', $arr) && $arr['status']; 109 | } 110 | 111 | return false; 112 | } 113 | 114 | /** 115 | * 复制文件 116 | * 117 | * @param $key 118 | * @param $newKey 119 | * @return bool 120 | */ 121 | public function copy($key, $newKey) 122 | { 123 | $client = new Client(); 124 | 125 | $res = $client->post($this->api, $this->appendSign(array( 126 | 'action' => 'copy', 127 | 'key' => $key, 128 | 'newKey' => $newKey, 129 | ))); 130 | 131 | $this->error = $res->getBody(); 132 | 133 | if ($res->getStatusCode() == 200) { 134 | $arr = @json_decode($res->getBody(), true); 135 | return is_array($arr) && array_key_exists('status', $arr) && $arr['status']; 136 | } 137 | 138 | return false; 139 | 140 | } 141 | 142 | 143 | /** 144 | * 删除文件 145 | * 146 | * @param $key 147 | * @return bool 148 | */ 149 | public function delete($key) 150 | { 151 | $client = new Client(); 152 | 153 | $res = $client->post($this->api, $this->appendSign(array( 154 | 'action' => 'delete', 155 | 'key' => $key, 156 | ))); 157 | 158 | $this->error = $res->getBody(); 159 | 160 | if ($res->getStatusCode() == 200) { 161 | $arr = @json_decode($res->getBody(), true); 162 | return is_array($arr) && array_key_exists('status', $arr) && $arr['status']; 163 | } 164 | 165 | return false; 166 | } 167 | 168 | /** 169 | * 错误消息 170 | * 171 | * @return string 172 | */ 173 | public function error() 174 | { 175 | return $this->error; 176 | } 177 | 178 | } 179 | 180 | -------------------------------------------------------------------------------- /src/MyHttpTrait.php: -------------------------------------------------------------------------------- 1 | doUpload($request); 15 | //} 16 | 17 | trait MyHttpTrait 18 | { 19 | protected function getBaseDir() 20 | { 21 | return 'temp'; 22 | } 23 | 24 | //子类重写这个方法 25 | protected function checkSign(Request $request) 26 | { 27 | throw new \Exception('sign error'); 28 | // $info = [ 29 | // //$accessKey => $secretKey 30 | // '1001' => '289dff07669d7a23de0ef88d2f7129e7' 31 | // ]; 32 | // 33 | // $accessKey = $request->get('accessKey'); 34 | // $timestamp = $request->get('timestamp'); 35 | // $nonce = $request->get('nonce'); 36 | // 37 | // $sign = $request->get('sign'); 38 | // 39 | // if (!array_key_exists($accessKey, $info)) { 40 | // throw new \Exception('sign error'); 41 | // } 42 | // 43 | // //验证token 44 | // if ($sign !== $this->doSign($accessKey, $timestamp, $nonce, $info[$accessKey])) { 45 | // throw new \Exception('sign error'); 46 | // } 47 | } 48 | 49 | protected function doSign($accessKey, $timestamp, $nonce, $secretKey) 50 | { 51 | //验证token 52 | return md5($accessKey . $timestamp . $nonce . $secretKey); 53 | } 54 | 55 | public function doUpload(Request $request) 56 | { 57 | $this->checkSign($request); 58 | 59 | $action = $request->get('action', 'upload'); 60 | if ($action == 'upload') { 61 | return $this->_doUpload($request); 62 | } 63 | 64 | if ($action == 'move') { 65 | return $this->doMove($request); 66 | } 67 | 68 | if ($action == 'delete') { 69 | return $this->doDelete($request); 70 | } 71 | 72 | if ($action == 'copy') { 73 | return $this->doCopy($request); 74 | } 75 | 76 | } 77 | 78 | private function _doUpload(Request $request) 79 | { 80 | 81 | $filename = $request->get('filename'); 82 | $this->checkFilename($filename); 83 | 84 | //文件存在则报错 85 | $fullName = rtrim($this->getBaseDir(), '/\\') . DIRECTORY_SEPARATOR . $filename; 86 | if (file_exists($fullName)) { 87 | return Json::renderWithFalse('file exists'); 88 | } 89 | 90 | if (!file_exists(dirname($fullName))) { 91 | mkdir(dirname($fullName), 0777, true); 92 | } 93 | 94 | //判断是否在baseDir目录下 95 | if (stripos(realpath(dirname($fullName)), realpath($this->getBaseDir())) !== 0) { 96 | return Json::renderWithFalse('filename error'); 97 | } 98 | 99 | $temp = $_FILES['file']['tmp_name']; 100 | if (move_uploaded_file($temp, $fullName)) { 101 | return Json::renderWithTrue(); 102 | } else { 103 | return Json::renderWithFalse(); 104 | } 105 | } 106 | 107 | private function checkFilename($filename) 108 | { 109 | // 必须限定在baseDir目录内,不能出现 .. 110 | // ([\w\-]+\/)* 目录可有可无 (字母 数字 下划线 中杠) 111 | // [\w\-]+ 文件名必须有 112 | // (\.\w+)* 扩展名可有可无 113 | if (!preg_match('/^([\w\-]+\/)*[\w\-]+(\.\w+)*$/', $filename)) { 114 | throw new \Exception('filename error'); 115 | } 116 | } 117 | 118 | private function doMove(Request $request) 119 | { 120 | $key = $request->get('key'); 121 | $newFile = $request->get('newKey'); 122 | 123 | $this->checkFilename($key); 124 | $this->checkFilename($newFile); 125 | 126 | $fullNameOld = rtrim($this->getBaseDir(), '/\\') . DIRECTORY_SEPARATOR . $key; 127 | if (!file_exists($fullNameOld)) { 128 | return Json::renderWithFalse('file not exists'); 129 | } 130 | 131 | $fullNameNew = rtrim($this->getBaseDir(), '/\\') . DIRECTORY_SEPARATOR . $newFile; 132 | 133 | if (!file_exists(dirname($fullNameNew))) { 134 | mkdir(dirname($fullNameNew), 0777, true); 135 | } 136 | 137 | //判断是否在baseDir目录下 138 | if (stripos(realpath(dirname($fullNameNew)), realpath($this->getBaseDir())) !== 0) { 139 | return Json::renderWithFalse('filename error'); 140 | } 141 | 142 | if (rename($fullNameOld, $fullNameNew)) { 143 | return Json::renderWithTrue(); 144 | } 145 | return Json::renderWithFalse(); 146 | } 147 | 148 | private function doCopy(Request $request) 149 | { 150 | $key = $request->get('key'); 151 | $newFile = $request->get('newKey'); 152 | 153 | $this->checkFilename($key); 154 | $this->checkFilename($newFile); 155 | 156 | $fullNameOld = rtrim($this->getBaseDir(), '/\\') . DIRECTORY_SEPARATOR . $key; 157 | if (!file_exists($fullNameOld)) { 158 | return Json::renderWithFalse('file not exists'); 159 | } 160 | 161 | $fullNameNew = rtrim($this->getBaseDir(), '/\\') . DIRECTORY_SEPARATOR . $newFile; 162 | 163 | if (!file_exists(dirname($fullNameNew))) { 164 | mkdir(dirname($fullNameNew), 0777, true); 165 | } 166 | 167 | //判断是否在baseDir目录下 168 | if (stripos(realpath(dirname($fullNameNew)), realpath($this->getBaseDir())) !== 0) { 169 | return Json::renderWithFalse('filename error'); 170 | } 171 | 172 | if (copy($fullNameOld, $fullNameNew)) { 173 | return Json::renderWithTrue(); 174 | } 175 | return Json::renderWithFalse(); 176 | } 177 | 178 | private function doDelete(Request $request) 179 | { 180 | $key = $request->get('key'); 181 | $this->checkFilename($key); 182 | 183 | $fullName = rtrim($this->getBaseDir(), '/\\') . DIRECTORY_SEPARATOR . $key; 184 | 185 | if (!file_exists($fullName)) { 186 | return Json::renderWithFalse('file not exists'); 187 | } 188 | 189 | //判断是否在baseDir目录下 190 | if (stripos(realpath(dirname($fullName)), realpath($this->getBaseDir())) !== 0) { 191 | return Json::renderWithFalse('filename error'); 192 | } 193 | 194 | unlink($fullName); 195 | } 196 | } -------------------------------------------------------------------------------- /src/Qiniu.php: -------------------------------------------------------------------------------- 1 | $item) { 22 | $this->$key = $item; 23 | } 24 | } 25 | 26 | /** 27 | * 上传文件 28 | * 29 | * @param string $key 30 | * @param $data 31 | * @return bool 32 | */ 33 | public function put($key, $data) 34 | { 35 | $key = $this->basePath . $key; 36 | 37 | $upManager = new UploadManager(); 38 | $auth = new Auth($this->accessKey, $this->secretKey); 39 | $token = $auth->uploadToken($this->bucketName); 40 | list($ret, $error) = $upManager->put($token, $key, $data); 41 | 42 | //失败情况下ret为null 43 | if ($ret == null) { 44 | $this->error = $error->message(); 45 | return false; 46 | } else { 47 | //echo $ret['hash']; //FizFMFnR5n7w8DvaFDQ4__RRXnJV 48 | //echo $ret['key']; //test/bmw.jpeg 49 | return true; 50 | } 51 | } 52 | 53 | /** 54 | * 返回文件外链 55 | * 56 | * @param $key 57 | * @param string|null $rule 处理规则,为null时原样返回 58 | * @return string 59 | */ 60 | public function url($key, $rule = null) 61 | { 62 | $key = $this->basePath . $key; 63 | 64 | if ($rule !== null) { 65 | $key = $key . $this->separator . $rule; 66 | } 67 | return $this->baseUrl . $key; 68 | } 69 | 70 | /** 71 | * 移动文件 72 | * 73 | * @param $key 74 | * @param $newKey 75 | * @return bool 76 | */ 77 | public function rename($key, $newKey) 78 | { 79 | $key = $this->basePath . $key; 80 | $newKey = $this->basePath . $newKey; 81 | 82 | //初始化Auth状态: 83 | $auth = new Auth($this->accessKey, $this->secretKey); 84 | 85 | //初始化BucketManager 86 | $bucketMgr = new BucketManager($auth); 87 | 88 | //确保这个key在你空间中存在 89 | $bucket = $this->bucketName; 90 | 91 | //将文件从文件$key 改成文件名$newKey 可以在不同bucket移动 92 | $err = $bucketMgr->move($bucket, $key, $bucket, $newKey); 93 | 94 | if ($err !== null) { 95 | $this->error = $err->message(); 96 | return false; 97 | } else { 98 | return true; 99 | } 100 | } 101 | 102 | /** 103 | * 复制文件 104 | * 105 | * @param $key 106 | * @param $newKey 107 | * @return bool 108 | */ 109 | public function copy($key, $newKey) 110 | { 111 | $key = $this->basePath . $key; 112 | $newKey = $this->basePath . $newKey; 113 | 114 | //初始化Auth状态: 115 | $auth = new Auth($this->accessKey, $this->secretKey); 116 | 117 | //初始化BucketManager 118 | $bucketMgr = new BucketManager($auth); 119 | 120 | //确保这个key在你空间中存在 121 | $bucket = $this->bucketName; 122 | 123 | $err = $bucketMgr->copy($bucket, $key, $bucket, $newKey); 124 | 125 | if ($err !== null) { 126 | $this->error = $err->message(); 127 | return false; 128 | } else { 129 | return true; 130 | } 131 | } 132 | 133 | 134 | /** 135 | * 删除文件 136 | * 137 | * @param $key 138 | * @return bool 139 | */ 140 | public function delete($key) 141 | { 142 | $key = $this->basePath . $key; 143 | 144 | //初始化Auth状态: 145 | $auth = new Auth($this->accessKey, $this->secretKey); 146 | 147 | //初始化BucketManager 148 | $bucketMgr = new BucketManager($auth); 149 | 150 | //确保这个key在你空间中存在 151 | $bucket = $this->bucketName; 152 | 153 | $err = $bucketMgr->delete($bucket, $key); 154 | 155 | if ($err !== null) { 156 | $this->error = $err->message(); 157 | return false; 158 | } else { 159 | return true; 160 | } 161 | } 162 | 163 | /** 164 | * 错误消息 165 | * 166 | * @return string 167 | */ 168 | public function error() 169 | { 170 | return $this->error; 171 | } 172 | 173 | public function getClientToken($prefix = 'uploads/') 174 | { 175 | $key = $prefix . md5(uniqid('', true) . time() . rand(100000, 999999)); 176 | 177 | $key = $this->basePath . $key; 178 | 179 | $policy = array( 180 | 'returnBody' => 181 | json_encode(array( 182 | "key" => '$(key)', 183 | "name" => '$(fname)', 184 | "size" => '$(fsize)', 185 | "mimeType" => '$(mimeType)', 186 | "etag" => '$(etag)', 187 | "width" => '$(imageInfo.width)', 188 | "height" => '$(imageInfo.height)', 189 | )) 190 | ); 191 | 192 | //初始化Auth状态: 193 | $auth = new Auth($this->accessKey, $this->secretKey); 194 | $token = $auth->uploadToken($this->bucketName, $key, 3600, $policy); 195 | 196 | return array('key' => $key, 'token' => $token); 197 | } 198 | } -------------------------------------------------------------------------------- /src/StorageInterface.php: -------------------------------------------------------------------------------- 1 | 'www.test.com', 7 | 'username' => 'ftpuser', 8 | 'password' => '123456', 9 | 10 | 'port' => 21, 11 | 'passive' => true, 12 | 'timeout' => 30, 13 | 14 | 'baseUrl' => 'http://static.it266.com/', 15 | ]; 16 | 17 | $ftp = new \PFinal\Storage\Ftp($config); 18 | $bool = $ftp->put('test/abc.jpg', file_get_contents('/Users/ethan/Pictures/1.jpg')); 19 | var_dump($bool); 20 | var_dump($ftp->url('test/abc.jpg')); 21 | var_dump($ftp->url('test/abc.jpg', 'm')); 22 | -------------------------------------------------------------------------------- /test/jquery-file-upload/cors/postmessage.html: -------------------------------------------------------------------------------- 1 | 2 | 14 | 15 |
16 | 17 |