Wanzul Blog

Notes, opinions, and lessons I paid for.

Object storage

如何使用纯 PHP 上传和下载 S3 文件(无需 SDK)

我熟悉 Google Drive 和 Microsoft OneDrive 这类典型的云存储服务。不过,在网页开发中,这些云存储提供商并不太合适,因为它们缺少指向已上传文件的直接、永久链接。为了解决这个问题,对象存储就成了一个方便又可靠的方案。

在 OneDrive 中,提供的链接会导向网页界面,而不是原始文件本身。我们需要热链接功能,才能在网页上有效地使用文件。

在本文中,我会使用 IPServerOne 提供的对象存储,而不是 Amazon S3。过程几乎相同;你只需要相应地更改 $s3_endpoint 变量。

小贴士:如果你不熟悉 PHP,可以请 ChatGPT/Copilot 将 PHP 代码逆向转换成 cURL 命令。


上传到 S3

要将文件上传到对象存储提供商,你只需要传入正确的参数并执行一个 cURL 请求。其实非常简单直接——就像发起典型的 REST API 调用一样。

关键逻辑: 上传逻辑的核心是使用 curl_init 打开连接到 S3 端点的连接,然后使用 CURLOPT_PUT 直接流式传输文件内容。这种方法非常高效,因为它避免了大型第三方库的开销。

<?php

$file_to_upload = 'yourimage.png';
$bucket = 'wordpressha';
$s3_endpoint = 'ap-southeast-mys1.oss.ips1cloud.com';
$access_key = '5IRK7V7TVSV0B829WHAD';
$secret_key = 'I7BLtOZAsHdCDHMeyJ73QipBIPjLDCvWhIAzfynV';

$content_type = 'application/octet-stream';
$date_value = gmdate('D, d M Y H:i:s T');

$string_to_sign = "PUTnn{$content_type}n{$date_value}n/{$bucket}/{$file_to_upload}";
$signature = base64_encode(hash_hmac('sha1', $string_to_sign, $secret_key, true));

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://{$bucket}.{$s3_endpoint}/{$file_to_upload}");
curl_setopt($ch, CURLOPT_PUT, true);
curl_setopt($ch, CURLOPT_INFILE, fopen($file_to_upload, 'r'));
curl_setopt($ch, CURLOPT_INFILESIZE, filesize($file_to_upload));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
  "Host: {$bucket}.{$s3_endpoint}",
  "Date: {$date_value}",
  "Content-Type: {$content_type}",
  "Authorization: AWS {$access_key}:{$signature}"
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

if ($http_code == 200) {
  echo "File uploaded successfully.";
} else {
  echo "Failed to upload file. HTTP Status Code: {$http_code}";
}

演示 💻

这个演示展示了文件如何直接上传到对象存储:

简单直接。你无需仅仅为了上传文件到 S3 而导入任何笨重的库。

访问私有存储桶文件(从 S3 下载)

存储在公共存储桶中的对象(文件)可以通过其 URL 直接访问。不过,对于私有存储桶,URL 需要在查询字符串中加入额外的身份验证参数才能授予访问权限。

关键逻辑: 生成预签名 URL 的方法是使用 hash_hmac 和你的 Secret Key 对过期时间及存储桶路径进行哈希运算。由于这是客户端计算,此步骤不需要调用 API,因此速度极快。

<?php

$object_key = 'yourimage.png';
$bucket = 'wordpressha';
$s3_endpoint = 'ap-southeast-mys1.oss.ips1cloud.com';
$access_key = '5IRK7V7TVSV0B829WHAD';
$secret_key = 'I7BLtOZAsHdCDHMeyJ73QipBIPjLDCvWhIAzfynV';

$expires = time() + 3600;
$method = 'GET';

$string_to_sign = "{$method}nnn{$expires}n/{$bucket}/{$object_key}";
$signature = base64_encode(hash_hmac('sha1', $string_to_sign, $secret_key, true));

$presigned_url = "https://{$bucket}.{$s3_endpoint}/{$object_key}?AWSAccessKeyId={$access_key}&Expires={$expires}&Signature=" . urlencode($signature);

echo "Presigned URL: {$presigned_url}n";

通过公共存储桶访问对象的示例

https://ap-southeast-mys1.oss.ips1cloud.com/wordpressha/test.jpg

通过私有存储桶访问对象的示例

https://wordpressha.ap-southeast-mys1.oss.ips1cloud.com/test.jpg?AWSAccessKeyId=KL6FSI64D6FEBHY77621&Expires=1734687785&Signature=4zRuNtWz%2F9ewHT%2FUF7c2mCfOxco%3D

注意附加在 URL 后面的 AWSAccessKeyId、Expires 和 Signature 参数。这称为预签名 URL。

回看代码,你会注意到生成预签名 URL 并不涉及 API 调用。它纯粹是根据用你的 Secret Key 加盐的字符串所生成的哈希值来计算。

演示 💻

这个演示说明了如何访问私有存储桶中的文件:


不用担心尝试访问测试存储桶——访问权限已经被撤销。这些示例只是为了本文而创建 😉。

到这里,你应该已经清楚了解对象存储如何运作,以及如何用纯 PHP 将它集成起来!😇

Wan Zulkarnain 的头像

Written by 

Comments

发表回复