Home
Linux
Golang
MySQL
PHP
Other
阿里云oss服务器回调上传
创建日期:2022-06-15 16:59:02
更新日期:2023-12-01 14:57:34
栏目:
PHP
浏览:1252
[TOC] # 说明 传统上传是让前端将文件传给后端服务器,然后后端服务器再上传到 OSS,这样在上传一些大文件的时候,就会显得不足 服务器压力大,上传慢,容易网关超时等等 现在可以将这些压力等分摊到客户端和 oss 具体思路就是: - 前端要上传文件,找后端要签名 - 后端将签名给到前端,签名里面可以设置各种参数(上传目录,文件大小,类型,上传有效期,回调等) - 前端拿到这些参数签名后,构造 from 表单,将相应的 key,value 赋值后上传到指定的URL上 - 前端上传成功后,OSS会回调服务端的回调接口,服务端接口验签后告诉OSS成功或失败,可以返回相应的JSON等,然后OSS会原封不动的将这些信息给到前端,前端根据想要的返回来显示不同的逻辑 ## 图解:  # 附录-代码示例 ## 后端服务端代码 需要安装阿里云的 `composer` 包 `aliyuncs/oss-sdk-php` ``` /** * 上传文件方式之1,2,3,4 * 一、原始方式:前端传后端,后端传oss,绝大多数用的此方式 * 二、JS直接上传:不推荐,容易暴露密钥 * 三、服务器签名后,将相应的签名参数给前端,让前端直传到oss,这样服务器并不知道用户上传了什么,慎用,看使用场景 * 四、回调方式: * 4.1、前端找后端要签名(里面包含很多参数:如上传成功后的回调等) * 4.2、前端拿到签名后直传oss * 4.3、前端上传成功后,oss会回调后端接口,后端接受到回调后,验签等通过后,告诉oss成功或失败(可以自定义带返回参数过去) * 4.4、oss接受到后端的返回后,将相应的成功或失败信息告诉前端,前端根据成功或失败处理相应逻辑 * * 参考链接: * https://help.aliyun.com/document_detail/31924.html * https://help.aliyun.com/document_detail/31853.html */ /** * Description: 上传方式4,主要介绍该方法 * Author: Shuxiaoyuan * Email: sxy@shuxiaoyuan.com * DateTime: 2022/6/15 15:38 * * @param Request $request * * @return array|\Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View * @throws \Exception * * 参考链接:https://help.aliyun.com/document_detail/91771.html */ public function method4(Request $request) { $id = $this->accessKeyId; $key = $this->accessKeySecret; // $host的格式为 bucketname.endpoint,请替换为您的真实信息。 $host = 'https://' . $this->bucket . '.' . $this->endpoint; // $callbackUrl为上传回调服务器的URL,请将下面的IP和Port配置为您自己的真实URL信息。 $callbackUrl = $this->callbackUrl; // 用户上传文件时指定的前缀,这样文件就只能上传到 test 文件夹下面 $dir = 'test/' . date('Ymd') . '/'; // 用户唯一ID,主要是为了回调确认用户身份的 $uid = mt_rand(1, 999); // 用户姓名,原理同 uid $username = Str::random(32); $callback_param = [ 'callbackUrl' => $callbackUrl, /** * 回调的 body,里面可以配置一些信息来确定用户身份唯一性 * 如下设置,上传图片后返回的信息如下:{"uid":"666","name":"shuxiaoyuan","filename":"test\\/20220615\\/K4Ae7eZjHm.jpg","size":"4312532","mimeType":"image\\/jpeg","height":"2160","width":"3840","s":"\\/\\/demo\\/ali\\/oss\\/method4Notify"} */ 'callbackBody' => 'uid=' . $uid . '&name=' . $username . '&filename=${object}&size=${size}&mimeType=${mimeType}&height=${imageInfo.height}&width=${imageInfo.width}', 'callbackBodyType' => "application/x-www-form-urlencoded" ]; $callback_string = json_encode($callback_param); $base64_callback_body = base64_encode($callback_string); // 当前时间 $now = time(); // 设置该 policy 超时时间(秒),即这个 policy 过了这个有效时间,将不能访问。 $expire = 3600; $end = $now + $expire; $expiration = $this->gmt_iso8601($end); // 最大文件大小.用户可以自己设置 $condition = array( 0 => 'content-length-range', 1 => 0, 2 => 1024 * 1024 * 1000 // 1000M ); $conditions[] = $condition; // 表示用户上传的数据,必须是以$dir开始,不然上传会失败,这一步不是必须项,只是为了安全起见,防止用户通过policy上传到别人的目录。 $start = array( 0 => 'starts-with', 1 => '$key', 2 => $dir ); $conditions[] = $start; $arr = array( 'expiration' => $expiration, 'conditions' => $conditions ); $policy = json_encode($arr); $base64_policy = base64_encode($policy); $string_to_sign = $base64_policy; $signature = base64_encode(hash_hmac('sha1', $string_to_sign, $key, true)); $response = array(); $response['accessid'] = $id; $response['host'] = $host; $response['policy'] = $base64_policy; $response['signature'] = $signature; $response['expire'] = $end; $response['callback'] = $base64_callback_body; $response['dir'] = $dir; // 这个参数是设置用户上传文件时指定的前缀。 if ($request->getMethod() == 'GET') { return view('ali/oss/method4', ['data' => $response]); } else { return $response; } } /** * Description: 方式4上传的回调处理方法 * Author: Shuxiaoyuan * Email: sxy@shuxiaoyuan.com * DateTime: 2022/5/9 12:10 * * @param Request $request * * @return array */ public function method4Notify(Request $request) { $response = $request->all(); // 1.获取OSS的签名header和公钥url header $authorizationBase64 = ""; $pubKeyUrlBase64 = ""; /* * 注意:如果要使用HTTP_AUTHORIZATION头,你需要先在apache或者nginx中设置rewrite,以apache为例,修改 * 配置文件/etc/httpd/conf/httpd.conf(以你的apache安装路径为准),在DirectoryIndex index.php这行下面增加以下两行 RewriteEngine On RewriteRule .* - [env=HTTP_AUTHORIZATION:%{HTTP:Authorization},last] * */ if (isset($_SERVER['HTTP_AUTHORIZATION'])) { $authorizationBase64 = $_SERVER['HTTP_AUTHORIZATION']; } if (isset($_SERVER['HTTP_X_OSS_PUB_KEY_URL'])) { $pubKeyUrlBase64 = $_SERVER['HTTP_X_OSS_PUB_KEY_URL']; } if ($authorizationBase64 == '' || $pubKeyUrlBase64 == '') { return Tools::outErrorInfo(__LINE__, '上传失败'); } // 2.获取OSS的签名 $authorization = base64_decode($authorizationBase64); // 3.获取公钥 $pubKeyUrl = base64_decode($pubKeyUrlBase64); $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $pubKeyUrl); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10); $pubKey = curl_exec($ch); if ($pubKey == "") { return Tools::outErrorInfo(__LINE__, '上传失败'); } // 4.获取回调body $body = file_get_contents('php://input'); // 5.拼接待签名字符串 $authStr = ''; $path = $_SERVER['REQUEST_URI']; $pos = strpos($path, '?'); if ($pos === false) { $authStr = urldecode($path) . "\n" . $body; } else { $authStr = urldecode(substr($path, 0, $pos)) . substr($path, $pos, strlen($path) - $pos) . "\n" . $body; } // 6.验证签名 $status = openssl_verify($authStr, $authorization, $pubKey, OPENSSL_ALGO_MD5); if ($status == 1) { header("Content-Type: application/json"); if (!$url = $this->cdnDomain) { $url = 'https://' . $this->bucket . '.' . $this->endpoint; } $data = [ 'u_id' => $response['uid'], // 这个地方应该拼接,不要写死 'file_url' => $url . '/' . $response['filename'], 'type' => 2, 'data' => json_encode($response) ]; return Tools::outSuccessInfo(['data' => $data]); } else { return Tools::outErrorInfo(__LINE__, '验签失败'); } } /** * 时间处理 * * @param $time * * @return string * @throws \Exception */ function gmt_iso8601($time) { $dtStr = date("c", $time); $mydatetime = new \DateTime($dtStr); $expiration = $mydatetime->format(\DateTime::ISO8601); $pos = strpos($expiration, '+'); $expiration = substr($expiration, 0, $pos); return $expiration . "Z"; } ``` ## 前端 HTML 代码 ``` <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <title>浏览器直传OSS</title> </head> <body> <h1>浏览器直传OSS(方式四,回调形式)</h1> <h2>参考链接:https://help.aliyun.com/document_detail/267439.html</h2> <h3>需要注意字段顺序</h3> <form action="{{ $data['host'] }}" method="post" enctype="multipart/form-data"> key(dir(只允许上传的目录)):<input name="key" value="{{$data['dir']}}"/><br/> policy:<input name="policy" value="{{$data['policy']}}"/><br/> accessid:<input name="OSSAccessKeyId" value="{{$data['accessid']}}"/><br/> callback(回调):<input name="callback" value="{{$data['callback']}}"/><br/> expire(过期时间):<input name="expire" value="{{ date('Y-m-d H:i:s',$data['expire']) }}"/><br/> signature(签名):<input name="signature" value="{{$data['signature']}}"/><br/> <input type="file" name="filename"/><br/> <br/> <input type="submit" name="submit" value="Upload to AliOss"/> </form> </body> </html> ```
内容版权声明:本文为舒孝元原创文章,转载无需和我联系,但请注明来自
舒孝元博客:https://www.shuxiaoyuan.com/info/96
联系邮箱:sxy@shuxiaoyuan.com