阿里云OSS服务端签名后前端JS直传(php)示例
需求:服务端上传图片太慢,需要使用前端直传的方式。但前端把阿里云的 AccessKey ID 和 AccessKey Secret 放在前端文件中会有风险,所以需要使用服务端获取签名在由前端上传文件到OSS。
环境介绍:Linux+Nginx+PHP7+Mysql5.7
一、OSS直传原理
1.前端发送Ajax请求PHP服务端,PHP服务端生成签名后返回给前端
2.前端带着服务端的签名,以及需要上传的File文件上传到OSS中
下面开始代码演示
二、代码演示
服务端获取签名代码
1.PHP服务端获取签名
前往阿里云官网根据官网提示配置阿里OSS
1.点我跳转:阿里OSS服务端配置介绍
2.点我跳转:阿里云官方应用服务端源码(PHP版本)
3.点我跳转:PostObject手册,查看前端上传OSS的必填参数
下载好应用服务端源码后先按照官网上的要求对OSS跨域进行配置。
- 开发编写PHP服务端代码
我们只需要图示上的2个文件。剩下的文件我们暂时用不到。代码我简单封装下,方便大家直接复制粘贴,直接就能使用。
PHP代码如下(示例):
<?php
//实例化
$obj = new GetOssController();
//代用获取签名方法
$obj->getOssSignature($_GET['dir']);
/**
* 获取OSS服务端签名类
* Created by PhpStorm.
* User: Administrator
* Date: 2021/3/10
* Time: 14:31
*/
class GetOssController
{
private $accessKeyId = '';//请填写您的AccessKeyId
private $accessKeySecret = '';//请填写您的AccessKeySecret
private $host = 'https://Bucket外网访问域名';//$host的格式为 bucketname.endpoint,请替换为您的真实信息 (就是Bucket域名-外网访问直接复制粘贴再此就行)
private $callbackUrl = 'http://你的域名/callback.php';//上传回调的地址 还记得上图中callback.php文件吗,把这文件放在你的项目中,配个路由保证能访问到这个文件就行,这个值就是访问callback.php此文件的URL 例如:我放在项目根目录 那值就为 http://liutong.pro/callback.php
/**
* 获取OSS应用服务端签名
* 这里都是官网的源码,我没什么改动
* @param (string)$dir : 上传到OSS的文件路径 如文件权路径为 image/001.png 那么 $dir = 'image/'; 即可
*/
public function getOssSignature($dir)
{
$id = $this->accessKeyId; // 请填写您的AccessKeyId。
$key = $this->accessKeySecret; // 请填写您的AccessKeySecret。
// $host的格式为 bucketname.endpoint,请替换为您的真实信息。
$host = $this->host;
// $callbackUrl为上传回调服务器的URL,请将下面的IP和Port配置为您自己的真实URL信息。
$callbackUrl = $this->callbackUrl;
$dir = (string)$dir; // 用户上传文件时指定的前缀。
$callback_param = array('callbackUrl' => $callbackUrl,
'callbackBody' => '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();
$expire = 30; //设置该policy超时时间是10s. 即这个policy过了这个有效时间,将不能访问。
$end = $now + $expire;
$expiration = $this->gmt_iso8601($end);
//最大文件大小.用户可以自己设置
$condition = array(0 => 'content-length-range', 1 => 0, 2 => 1048576000);
$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; // 这个参数是设置用户上传文件时指定的前缀。
echo json_encode($response);
}
private 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";
}
}
?>
private $host = 下图红框圈住的值。
注意:官方的callback.php文件我们不用改他,直接粘贴到你的项目中,确保可以用过路由访问到即可
2.Html端获取签名后上传文件到阿里OSS
前端代码如下(示例):
<!doctype html>
<html lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8"/>
<title>OSS web直传</title>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
</head>
<body>
<ul>
<li>
<span>上传至OSS的文件夹路径:</span>
<input type="text" name="dir" id="dir" value="test" placeholder="其填写上传目标文件夹名,OSS文件夹不存在时会自动生成,结尾不同加/">
</li>
<li>
<span>选择上传的文件:</span>
<input type="file" id="files"></li>
<li>
<button onclick="getOssSignature()">开始上传</button>
</li>
</ul>
</body>
<script src="./jquery.min.js"></script>
<!--OSS服务端签名后直传JS逻辑代码-->
<script>
//发送ajax获取服务端签名
function getOssSignature(){
var file = $('#files').get(0).files[0];//获取需要上传的文件对象
//检测文件是否存在
if(!file){
alert('请先选择文件');
return false;
}
//检测是否没有输入OSS文件路径
if(!$('#dir').val()){
alert('请填写上传至OSS的文件夹路径');
return false;
}
var dir = $('#dir').val() + '/';
//获取上传的文件名,并得到文件后缀
var location=$("#files").val();
var point = location.lastIndexOf(".");
var file_type = location.substr(point);//获取文件后缀
var file_name = dir + (new Date()).getTime() + file_type;//将OSS文件路径与新的文件名拼接在一起,生成新的路径+文件名
//发送ajax请求我方php后端获取上传OSS时必要的参数信息
$.ajax({
type : "get", //提交方式
url : "./GetOssController.php",//路径
data : {
"dir" : dir
},//数据,这里使用的是Json格式进行传输
success : function(res) {//返回数据根据结果进行相应的处理
res = JSON.parse(res);
//上返回的参数使用formData中
let formData = new FormData();
formData.append('key', file_name);
formData.append('OSSAccessKeyId', res.accessid);
formData.append('policy', res.policy);
formData.append('Signature', res.signature);
formData.append('callback', res.callback);
formData.append('success_action_status', 200); // 成功后返回的操作码
formData.append('file', file);
//接收到服务端返回的签名参数,开始通过另一个Ajax请求来上传文件到OSS
//成功获取签名后上传文件到阿里云OSS
$.ajax({
type : "POST", //提交方式
url : res.host,//路径
dataType:'XML',
processData: false,
cache: false,
async: false,
contentType: false,
//关键是要设置contentType 为false,不然发出的请求头 没有boundary
//该参数是让jQuery去判断contentType
data : formData,//要发送到OSS数据,使用我这个ajax的格式可避开跨域问题。
success : function(res2) {//返回数据根据结果进行相应的处理
console.log(res2);//返回success:ok 说明你就上传成功了
}
});
}
});
}
</script>
</html>