阿里云oss,通过html直接上传

环境:centOS stream 9\php8.3\thinkphp8.1

前端代码:

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>服务端生成签名上传文件到OSS</title>
</head>
<body>
<div class="container">
    <form>
        <div class="mb-3">
            <label for="file" class="form-label">选择文件:</label>
            <input type="file" class="form-control" id="file" name="file" required />
        </div>
        <button type="submit" class="btn btn-primary">上传</button>
    </form>
    <div id="callback-info" class="mt-3" style="display: none;">
        <h4>回调信息:</h4>
        <pre id="callback-content"></pre>
    </div>
</div>

<script type="text/javascript">
document.addEventListener('DOMContentLoaded', function () {
    const form = document.querySelector("form");
    const fileInput = document.querySelector("#file");
    const callbackInfo = document.querySelector("#callback-info");
    const callbackContent = document.querySelector("#callback-content");

    form.addEventListener("submit", (event) => {
        event.preventDefault();

        const file = fileInput.files[0];

        if (!file) {
            alert('请选择一个文件再上传。');
            return;
        }

        const filename = file.name;

        fetch("/AliyunOss/getUploadSignature/", { method: "GET" })
            .then((response) => {
                if (!response.ok) {
                    throw new Error("获取签名失败");
                }
                return response.json();
            })
            .then((data) => {
                let formData = new FormData();
                formData.append("success_action_status", "200");
                formData.append("policy", data.policy);
                formData.append("x-oss-signature", data.signature);
                formData.append("x-oss-signature-version", "OSS4-HMAC-SHA256");
                formData.append("x-oss-credential", data.x_oss_credential);
                formData.append("x-oss-date", data.x_oss_date);
                formData.append("key", data.dir + file.name); // 文件名
                formData.append("x-oss-security-token", data.security_token);
                formData.append("callback", data.callback);  // 添加回调参数
                formData.append("file", file); // file 必须为最后一个表单域

                return fetch(data.host, { 
                    method: "POST",
                    body: formData
                });
            })
            .then((response) => {
                if (response.ok) {
                    console.log("上传成功");
                    alert("文件已上传");
                    return response.json();  // 解析回调信息
                } else {
                    console.log("上传失败", response);
                    alert("上传失败,请稍后再试");
                }
            })
            .then((callbackData) => {
                if (callbackData) {
                    callbackContent.textContent = JSON.stringify(callbackData, null, 2);
                    callbackInfo.style.display = "block";
                }
            })
            .catch((error) => {
                console.error("发生错误:", error);
            });
    });
});
</script>
</body>
</html>

后端:

php 复制代码
use AlibabaCloud\Oss\V2 as Oss;
use AlibabaCloud\Client\AlibabaCloud;
use AlibabaCloud\Client\Exception\ClientException;
use AlibabaCloud\Client\Exception\ServerException;
use AlibabaCloud\Sts\Sts;

use think\facade\Log;

class AliyunOss
{
    private string $bucket;
    private string $region;
    private string $host;
    private int $expire;
    private string $uploadDir;
    private string $callbackUrl;

    public function __construct()
    {
        $this->bucket    = '你的bucket';
        $this->region    = 'cn-shanghai';
        $this->host      = 'http://你的bucket.oss-cn-shanghai.aliyuncs.com';
        $this->expire    = 3600;
        $this->uploadDir = '2005'; // 上传的文件名前缀
        $this->callbackUrl = 'http://go.xxxx.cn/AliyunOss/callback'; // 回调地址
        AlibabaCloud::accessKeyClient('你的AccessKey ID','你的AccessKey Secret')->regionId($this->region)->asDefaultClient();
    }

    private function hmacsha256($key, $data)
    {
        return hash_hmac('sha256', $data, $key, true);
    }

    /**
     * 获取 OSS V4 直传签名
     */
    public function getUploadSignature()
    {
        // 1. 获取 STS 临时凭证
        $result = Sts::v20150401()->assumeRole()
            ->withRoleSessionName('oss-role-session')
            ->withDurationSeconds($this->expire)
            ->withRoleArn('acs:ram::1241287589505885:role/oss')
            ->request();

        $tokenData   = $result->get('Credentials');
        
        // 构建返回的JSON数据。
        $tempAccessKeyId = $tokenData['AccessKeyId'];
        $tempAccessKeySecret = $tokenData['AccessKeySecret'];
        $securityToken = $tokenData['SecurityToken'];

        // 2. 生成时间字段
        $now = time();
        $dtObj = gmdate('Ymd\THis\Z', $now);
        $dtObj1 = gmdate('Ymd', $now);
        $dtObjPlus3h = gmdate('Y-m-d\TH:i:s.u\Z', strtotime('+3 hours', $now));
    
        // 构建Policy
        $policy = [
            "expiration" => $dtObjPlus3h,
            "conditions" => [
                ["x-oss-signature-version" => "OSS4-HMAC-SHA256"],
                ["x-oss-credential" => "{$tempAccessKeyId}/{$dtObj1}/cn-shanghai/oss/aliyun_v4_request"],
                ["x-oss-security-token" => $securityToken],
                ["x-oss-date" => $dtObj],
            ]
        ];
    
        $policyStr = json_encode($policy);
    
        // 构造待签名字符串
        $stringToSign = base64_encode($policyStr);
    
        // 计算SigningKey
        $dateKey = $this->hmacsha256(('aliyun_v4' . $tempAccessKeySecret), $dtObj1);
        $dateRegionKey = $this->hmacsha256($dateKey, 'cn-shanghai');
        $dateRegionServiceKey = $this->hmacsha256($dateRegionKey, 'oss');
        $signingKey = $this->hmacsha256($dateRegionServiceKey, 'aliyun_v4_request');
    
        // 计算Signature
        $result = $this->hmacsha256($signingKey, $stringToSign);
        $signature = bin2hex($result);
        
        $callback_param = array(
            'callbackUrl' => $this->callbackUrl,
            'callbackBody' => 'filename=${object}&size=${size}&mimeType=${mimeType}&height=${imageInfo.height}&width=${imageInfo.width}',
            'callbackBodyType' => "application/json"
        );
        $callback_string = json_encode($callback_param);
    
        $base64_callback_body = base64_encode($callback_string);
    
        // 返回签名数据
        $responseData = [
            'policy' => $stringToSign,
            'x_oss_signature_version' => "OSS4-HMAC-SHA256",
            'x_oss_credential' => "{$tempAccessKeyId}/{$dtObj1}/cn-shanghai/oss/aliyun_v4_request",
            'x_oss_date' => $dtObj,
            'signature' => $signature,
            'host' => $this->host,
            'dir' => $this->uploadDir,
            'security_token' => $securityToken,
            'callback' => $base64_callback_body,
        ];
        return json($responseData);
    }
    
    public function callback()
    {
        $data = file_get_contents("php://input");
        Log::info('OSS Callback => ' . $data);
    
        return json([
            'status' => 'OK',
            'msg' => '上传成功'
        ]);
    }
    
    public function index()
    {
        return view('/upload'); // 渲染前端页面upload.html;
    }
}