赛尔校园公共服务平台 Logo
平台使用
阿里云
百度云
移动云
智算服务
教育生态
登录 →
赛尔校园公共服务平台 Logo
平台使用 阿里云 百度云 移动云 智算服务 教育生态
登录
  1. 首页
  2. 阿里云
  3. 对象存储
  4. 开发参考
  5. SDK参考
  6. PHP
  7. 2.0手册(预览版)
  8. 对象文件
  9. 上传文件
  10. 表单上传

表单上传

  • 上传文件
  • 发布于 2025-04-21
  • 0 次阅读
文档编辑
文档编辑

OSS表单上传允许网页应用通过标准HTML表单直接将文件上传至OSS。本文介绍如何使用PHP SDK V2生成Post签名和Post Policy等信息,并调用HTTP Post方法上传文件到OSS。

注意事项

  • 本文示例代码以华东1(杭州)的地域IDcn-hangzhou为例,默认使用外网Endpoint,如果您希望通过与OSS同地域的其他阿里云产品访问OSS,请使用内网Endpoint。关于OSS支持的Region与Endpoint的对应关系,请参见OSS地域和访问域名。

  • 通过表单上传的方式上传的Object大小不能超过5 GB。

示例代码

以下代码示例实现了表单上传的完整过程,主要步骤如下:

  1. 创建Post Policy:定义上传请求的有效时间和条件,包括存储桶名称、签名版本、凭证信息、请求日期和请求体长度范围。

  2. 序列化并编码Policy:将Policy序列化为JSON字符串,并进行Base64编码。

  3. 生成签名密钥:使用HMAC-SHA256算法生成签名密钥,包括日期、区域、产品和请求类型。

  4. 计算签名:使用生成的密钥对Base64编码后的Policy字符串进行签名,并将签名结果转换为十六进制字符串。

  5. 构建请求体:创建一个multipart表单写入器,添加对象键、策略、签名版本、凭证信息、请求日期和签名到表单中,并将要上传的数据写入表单。

  6. 创建并执行请求:创建一个HTTP POST请求,设置请求头,并发送请求,检查响应状态码确保请求成功。

<?php

// 引入自动加载文件,确保依赖库能够正确加载
require_once __DIR__ . '/../vendor/autoload.php';

use AlibabaCloud\Oss\V2 as Oss;

// 定义命令行参数的描述信息
$optsdesc = [
    "region" => ['help' => 'The region in which the bucket is located.', 'required' => True], // Bucket所在的地域(必填)
    "bucket" => ['help' => 'The name of the bucket', 'required' => True], // Bucket名称(必填)
    "key" => ['help' => 'The name of the object', 'required' => True], // 对象名称(必填)
];

// 将参数描述转换为getopt所需的长选项格式
// 每个参数后面加上":"表示该参数需要值
$longopts = \array_map(function ($key) {
    return "$key:";
}, array_keys($optsdesc));

// 解析命令行参数
$options = getopt("", $longopts);

// 验证必填参数是否存在
foreach ($optsdesc as $key => $value) {
    if ($value['required'] === True && empty($options[$key])) {
        $help = $value['help']; // 获取参数的帮助信息
        echo "Error: the following arguments are required: --$key, $help" . PHP_EOL;
        exit(1); // 如果必填参数缺失,则退出程序
    }
}

// 从解析的参数中提取值
$region = $options["region"]; // Bucket所在的地域
$bucket = $options["bucket"]; // Bucket名称
$key = $options["key"];       // 对象名称
$product = 'oss';             // 固定为OSS服务

// 加载环境变量中的凭证信息
// 使用EnvironmentVariableCredentialsProvider从环境变量中读取Access Key ID和Access Key Secret
$credentialsProvider = new Oss\Credentials\EnvironmentVariableCredentialsProvider();
$cred = $credentialsProvider->getCredentials();

// 要上传的数据内容
$data = 'hi oss'; // 示例数据,实际使用时可以替换为其他内容

// 获取当前UTC时间并格式化
$utcTime = new DateTime('now', new DateTimeZone('UTC'));
$date = $utcTime->format('Ymd'); // 当前日期,用于签名计算
$expiration = clone $utcTime;
$expiration->add(new DateInterval('PT1H')); // 设置过期时间为1小时后

// 构建Policy文档
$policyMap = [
    "expiration" => $expiration->format('Y-m-d\TH:i:s.000\Z'), // Policy的过期时间
    "conditions" => [
        ["bucket" => $bucket], // 指定Bucket名称
        ["x-oss-signature-version" => "OSS4-HMAC-SHA256"], // 签名版本
        ["x-oss-credential" => sprintf("%s/%s/%s/%s/aliyun_v4_request",
            $cred->getAccessKeyId(), $date, $region, $product)], // 凭证信息
        ["x-oss-date" => $utcTime->format('Ymd\THis\Z')], // 当前时间戳
        // 其他条件
        ["content-length-range", 1, 1024], // 文件大小范围限制
        // ["eq", "$success_action_status", "201"], // 可选:指定成功状态码
        // ["starts-with", "$key", "user/eric/"], // 可选:指定对象Key的前缀
        // ["in", "$content-type", ["image/jpg", "image/png"]], // 可选:指定允许的内容类型
        // ["not-in", "$cache-control", ["no-cache"]], // 可选:排除某些缓存控制头
    ],
];

// 将Policy文档编码为JSON字符串
$jsonOptions = JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE; // 防止转义斜杠和Unicode字符
$policy = json_encode($policyMap, $jsonOptions);
if (json_last_error() !== JSON_ERROR_NONE) {
    error_log("json_encode fail, err: " . json_last_error_msg()); // 检查JSON编码是否失败
    exit(1);
}

// 计算签名所需的信息
$stringToSign = base64_encode($policy); // 对Policy进行Base64编码
$signingKey = "aliyun_v4" . $cred->getAccessKeySecret(); // 构造签名密钥
$h1Key = hmacSign($signingKey, $date); // 第一步:对日期签名
$h2Key = hmacSign($h1Key, $region);   // 第二步:对区域签名
$h3Key = hmacSign($h2Key, $product);  // 第三步:对产品签名
$h4Key = hmacSign($h3Key, "aliyun_v4_request"); // 第四步:对请求签名

// 计算最终签名
$signature = hash_hmac('sha256', $stringToSign, $h4Key);

// 构建POST请求的表单数据
$bodyWriter = new CURLFileUpload(); // 创建表单构建器实例

// 添加字段到表单
$bodyWriter->addField('key', $key); // 对象名称
$bodyWriter->addField('policy', $stringToSign); // Base64编码后的Policy
$bodyWriter->addField('x-oss-signature-version', 'OSS4-HMAC-SHA256'); // 签名版本
$bodyWriter->addField('x-oss-credential', sprintf("%s/%s/%s/%s/aliyun_v4_request",
    $cred->getAccessKeyId(), $date, $region, $product)); // 凭证信息
$bodyWriter->addField('x-oss-date', $utcTime->format('Ymd\THis\Z')); // 时间戳
$bodyWriter->addField('x-oss-signature', $signature); // 最终签名

// 添加文件内容到表单
$bodyWriter->addFileFromString('file', $data); // 上传的文件内容
$postData = $bodyWriter->getFormData(); // 获取完整的表单数据

// 发送POST请求
$client = new \GuzzleHttp\Client(); // 创建HTTP客户端
$response = $client->post(
    sprintf("http://%s.oss-%s.aliyuncs.com/", $bucket, $region), // OSS的上传地址
    [
        'headers' => [
            'content-type' => $bodyWriter->getContentType(), // 设置Content-Type
        ],
        'body' => $postData // 设置请求体
    ]
);

// 检查响应状态码
if ($response->getStatusCode() < 200 || $response->getStatusCode() >= 300) {
    echo "Post Object Fail, status code:" . $response->getStatusCode() . ", reason: " . $response->getReasonPhrase() . PHP_EOL;
    exit(1); // 如果状态码不在2xx范围内,则退出程序
}

// 打印上传结果
echo "post object done, status code:" . $response->getStatusCode() . ", request id:" . $response->getHeaderLine('x-oss-request-id') . PHP_EOL;

/**
 * HMAC签名函数
 * @param string $key 签名密钥
 * @param string $data 待签名数据
 * @return string 返回签名结果
 */
function hmacSign($key, $data)
{
    return hash_hmac('sha256', $data, $key, true); // 使用SHA256算法生成HMAC签名
}

/**
 * 表单构建器类,用于生成multipart/form-data格式的请求体
 */
class CURLFileUpload
{
    private $fields = []; // 存储普通字段
    private $files = [];  // 存储文件字段
    private $boundary;    // 分隔符

    public function __construct()
    {
        $this->boundary = uniqid(); // 生成唯一的分隔符
    }

    /**
     * 添加普通字段
     * @param string $name 字段名称
     * @param string $value 字段值
     */
    public function addField($name, $value)
    {
        $this->fields[$name] = $value;
    }

    /**
     * 添加文件字段
     * @param string $name 字段名称
     * @param string $content 文件内容
     */
    public function addFileFromString($name, $content)
    {
        $this->files[$name] = [
            'content' => $content,
            'filename' => $name,
            'type' => 'application/octet-stream' // 默认MIME类型
        ];
    }

    /**
     * 获取完整的表单数据
     * @return string 返回multipart/form-data格式的请求体
     */
    public function getFormData()
    {
        $data = '';
        foreach ($this->fields as $name => $value) {
            $data .= "--{$this->boundary}\r\n";
            $data .= "Content-Disposition: form-data; name=\"$name\"\r\n\r\n";
            $data .= $value . "\r\n";
        }
        foreach ($this->files as $name => $file) {
            $data .= "--{$this->boundary}\r\n";
            $data .= "Content-Disposition: form-data; name=\"$name\"; filename=\"{$file['filename']}\"\r\n";
            $data .= "Content-Type: {$file['type']}\r\n\r\n";
            $data .= $file['content'] . "\r\n";
        }
        $data .= "--{$this->boundary}--\r\n";
        return $data;
    }

    /**
     * 获取Content-Type头部
     * @return string 返回Content-Type值
     */
    public function getContentType()
    {
        return "multipart/form-data; boundary={$this->boundary}";
    }
}

相关文档

  • 关于表单上传的完整示例,请参见Github示例。

相关文章

简单上传 2025-04-21 18:14

本文介绍如何通过简单上传方法将本地文件快速上传到OSS,此方法操作简便,适合快速将本地文件上传到云端存储。 注意事项 本文示例代码以华东1(杭州)的地域IDcn-hangzhou为例,默认使用外网En

追加上传 2025-04-21 18:14

追加上传是指在已上传的追加类型文件(Appendable Object)末尾直接追加内容。本文介绍如何使用OSS PHP SDK进行追加上传。 注意事项 本文示例代码以华东1(杭州)的地域IDcn-hangzhou

分片上传 2025-04-21 18:14

OSS提供的分片上传(Multipart Upload)功能,将要上传的较大文件(Object)分成多个分片(Part)来分别上传,上传完成后再调用CompleteMultipartUpload接口将这些Part组合成一个Object。 注意事项 本文示例代码以华东1(杭州)的地域ID

表单上传 2025-04-21 18:14

OSS表单上传允许网页应用通过标准HTML表单直接将文件上传至OSS。本文介绍如何使用PHP SDK V2生成Post签名和Post Policy等信息,并调用HTTP Post方法上传文件到OSS。 注意事项 本文示例代码以华东1(杭州)的地域ID

使用预签名URL上传 2025-04-21 18:14

默认情况下,OSS Bucket中的文件是私有的,仅文件拥有者可访问。本文介绍如何使用OSS PHP SDK生成带有过期时间的PUT方法预签名URL,以允许他人临时上传文件。在有效期内可多次访问,超期后需重新生成。 注意事项 本文示例代码以华东1(杭州)的地域ID

文件上传管理器 2025-04-21 18:14

本文针对文件的传输场景,介绍如何使用PHP SDK V2新增的上传管理器Uploader模块进行文件上传。 注意事项 本文示例代码以华东1(杭州)的地域ID

目录
Copyright © 2025 your company All Rights Reserved. Powered by 赛尔网络.
京ICP备14022346号-15
gongan beian 京公网安备11010802041014号