目录
文档
1、阿里云OSS上传方式
1.1、Web端Browser.js SDK直传(不推荐)
该方法会将AccessKey ID和AccessKey Secret直接保存在浏览器端,存在极高的风险
1.2、Web端上传服务端再上传至OSS
这种方式上传速度慢
1.3、服务端签名后Web端直传(推荐)
该方式安全性和上传速度都不错,本文采用此方式上传
2、PHP服务端生成签名
本例服务端签名生成代码通过官网给出的示例修改而来,基于ThinkPHP框架
使用了第三方库 ramsey/uuid
生成文件名
安装
composer require ramsey/uuid
官网给的示例有callback
参数,如果是前端使用签名直接传OSS是不需要这个参数的
结合官网给的Node.js示例,改造PHP代码
AliOssService.php
<?php namespace app\service; use Ramsey\Uuid\Uuid; /** * Class AliOssService * @package app\service * * 微信小程序直传实践 * @see https://help.aliyun.com/document_detail/92883.html */ class AliOssService { // 配置oss参数 private const AccessKeyId = '<AccessKeyId>'; private const AccessKeySecret = '<AccessKeySecret>'; private const Host = 'https://<region>.oss-cn-beijing.aliyuncs.com'; // 签名有效期 单位: 秒 private const Expire = 3 * 60; // 允许上传的文件最大和最小范围 单位:字节 private const ContentLengthMin = 0; private const ContentLengthMax = 20 * 1024 * 1024; /** * 获取服务端签名方式上传参数 * @param $params array * ext string 扩展名 eg: jpg * dirname string 上传目录 eg: image * @return array * @throws \Exception */ public static function getUploadParams($params) { // 接收参数 $ext = $params['ext']; $dirname = $params['dirname']; // 文件路径和文件名 $dir = self::getDirname($dirname); $key = $dir . self::getFilename($ext); // 过期时间 $expiration = self::getExpireTime(self::Expire); // 参数设置 // 附录:Post Policy // https://help.aliyun.com/document_detail/31988.htm#section-d5z-1ww-wdb $policyParams = [ 'expiration' => $expiration, 'conditions' => [ // 指定前缀 ['starts-with', '$key', $dir], // 限制上传文件大小。单位:字节 ['content-length-range', self::ContentLengthMin, self::ContentLengthMax] ] ]; $policyBase64 = self::getPolicyBase64($policyParams); $signature = self::getSignature($policyBase64, self::AccessKeySecret); return [ 'accessKeyId' => self::AccessKeyId, 'host' => self::Host, 'policy' => $policyBase64, 'signature' => $signature, 'expire' => $expiration, 'key' => $key, 'url' => self::Host . '/' . $key ]; } /** * 获取参数base64 * @param $policyParams array * @return string */ public static function getPolicyBase64($policyParams) { return base64_encode(json_encode($policyParams)); } /** * 获取签名 * @param $policyBase64 string * @param $accessKeySecret string * @return string */ public static function getSignature($policyBase64, $accessKeySecret) { return base64_encode(hash_hmac('sha1', $policyBase64, $accessKeySecret, true)); } /** * 获取过期时间 * @param $time int 单位: 秒 * @return mixed */ public static function getExpireTime($time) { return str_replace('+00:00', '.000Z', gmdate('c', time() + $time)); } /** * 获取按照月份分隔的文件夹路径 * @param $dirname string eg: image/video * @return string eg: image/2022-10/ */ public static function getDirname($dirname) { return $dirname . '/' . date('Y-m') . '/'; } /** * 获取一个随机的文件名 * @param $ext string eg: jpg * @return string eg: a4030d9f-c4a2-4f1a-8e33-80e017e572d5.jpg * @throws \Exception */ public static function getFilename($ext) { $uuid = Uuid::uuid4()->toString(); return $uuid . '.' . $ext; } }
AliOssController.php
<?php namespace app\controller; use app\BaseController; use app\exception\AppException; use app\service\AliOssService; class AliOssController extends BaseController { public function getUploadParams() { $ext = input('ext'); $dirname = input('dirname', 'image'); // 参数校验 if (!$ext) { throw new AppException('ext is empty'); } if (!in_array($dirname, ['image', 'video'], true)) { throw new AppException('dirname: only allow image or video'); } $result = AliOssService::getUploadParams([ 'ext' => $ext, 'dirname' => $dirname, ]); return $result; } }
AppException.php
<?php namespace app\exception; use Exception; /** * 自定义的业务异常 * Class AppException * @package app\exception */ class AppException extends Exception { }
3、微信小程序客户端
参考官网给出的示例实现
思路:
客户端拿到文件名后缀后,传给服务端,获取签名和文件名等必要的上传参数,让更多的工作在服务端完成
oss-upload-file.js
// 获取文件扩展名 function getFilePathExtention(filePath) { return filePath.split('.').slice(-1)[0]; } // 上传到阿里云oss function uploadFileAsync(config, filePath) { console.log(config); return new Promise((resolve, reject) => { wx.uploadFile({ url: config.host, // 开发者服务器的URL。 filePath: filePath, name: 'file', // 必须填file。 formData: { key: config.key, policy: config.policy, OSSAccessKeyId: config.accessKeyId, signature: config.signature, // 'x-oss-security-token': securityToken // 使用STS签名时必传。 }, success: (res) => { console.log(res); if (res.statusCode === 204) { resolve(); } else { reject('上传失败'); } }, fail: (err) => { // console.log(err); reject(err); }, }); }); } // 上传文件 export async function uploadFile(filePath, dirname = 'image') { console.log(filePath); let ext = getFilePathExtention(filePath); // 改方法通过接口获取服务端生成的上传签名 const resParams = await Http.AliOssGetUploadParams({ ext, dirname, }); // console.log(resParams.data); // let objectName = resParams.data.uuid + '.' + getFilePathExtention(filePath); await uploadFileAsync(resParams.data, filePath); // console.log(res); return resParams; }