环境: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;
}
}