Bootstrap

阿里云OSS服务端签名前端JS直传(php)示例

阿里云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跨域进行配置。

  1. 开发编写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>

End;

;