行业资讯

时间:2025-08-04 浏览量:(73)

PHP实现服务端签名阿里云OSS直传实践

Web 端通过表单直传数据到 OSS 的实现方案

一、概述

本教程介绍如何在 Web 端通过表单上传方式直接将数据上传到 OSS。传统的 Web 端上传流程是用户将文件上传到应用服务器,再由应用服务器转发至 OSS,这种方式经应用服务器中转,传输效率较低。而本文将采用服务端签名直传方式,即先在服务端生成签名,再由客户端使用该签名直接将文件上传到 OSS,不仅提高传输效率,还能避免访问密钥暴露在前端,安全性更高。

二、服务端签名直传流程

服务端签名直传的核心流程为:


客户端向服务端请求上传所需的签名及相关参数;

服务端生成上传策略、签名和回调信息,返回给客户端;

客户端使用获取到的签名和参数,通过表单直接将文件上传到 OSS;

上传完成后,OSS 根据配置的回调地址通知应用服务器,完成后续处理。

三、服务端实现:生成上传策略与签名

服务端的核心任务是生成上传策略、Base64 编码的策略字符串、签名以及回调参数,具体实现如下:

1. 获取上传策略签名(核心方法)

该方法整合了上传配置、路径生成、策略构建、签名计算和回调参数设置等功能:


php

/**

 * @desc 获取上传策略签名

 * @param array $param

 * @return array

 * @author Tinywan(ShaoBo Wan)

 */

public static function getUploadPolicy(array $param): array

{

    $param['type'] = 'images';

    $config = [

        'accessKeyId' => 'xxxxxxxxxxxxxxx',  // OSS访问密钥ID

        'accessKeySecret' => 'xxxxxxxxxxxxxxxxxxxxxxxxxxx',  // OSS访问密钥Secret

        'callbackUrl' => 'https://oss.tinywan.com/aliyun/oss-upload-callback',  // 上传回调地址

        'images' => [

            'bucket' => 'images',  // OSS存储桶名称

            'host' => 'https://images.tinywan.com',  // 访问域名

            'endpoint' => 'oss-cn-hangzhou.aliyuncs.com',  // OSS地域节点

            'ContentLengthMin' => 10,  // 最小文件大小(字节)

            'ContentLengthMax' => 20 * 1024 * 1024,  // 最大文件大小(20MB)

        ]

    ];

    $bucket = $config[$param['type']];

    

    // 生成文件路径和随机文件名

    $dir = self::PROJECT_BUCKET . DIRECTORY_SEPARATOR . env('env_name') . DIRECTORY_SEPARATOR 

        . self::getDirectoryPath($param['type'], $param['dirname']);  // 文件夹路径(按月份分类)

    $key = $dir . self::getRandomFilename($param['ext']);  // 完整文件路径(含随机文件名)

    

    // 计算过期时间(UTC格式)

    $expiration = self::getExpireTime(self::EXPIRE_TIME);

    

    // 构建上传策略

    $policyParams = [

        'expiration' => $expiration,  // 策略过期时间

        'conditions' => [

            ['starts-with', '$key', $dir],  // 限制文件路径前缀

            ['content-length-range', $bucket['ContentLengthMin'], $bucket['ContentLengthMax']]  // 限制文件大小范围

        ]

    ];

    $policyBase64 = self::getPolicyBase64($policyParams);  // 策略Base64编码

    $signature = self::getSignature($policyBase64, $config['accessKeySecret']);  // 计算签名

    

    // 构建回调参数

    $callbackParam = [

        'callbackUrl' => $config['callbackUrl'],

        'callbackBody' => 'filename=${object}&size=${size}&mimeType=${mimeType}&height=${imageInfo.height}&width=${imageInfo.width}',  // 回调参数

        'callbackBodyType' => 'application/x-www-form-urlencoded',  // 回调数据格式

    ];

    $callbackString = json_encode($callbackParam);

    $base64CallbackBody = base64_encode($callbackString);  // 回调参数Base64编码

    

    return [

        'access_id' => $config['accessKeyId'],

        'host' => 'https://' . $bucket['bucket'] . '.' . $bucket['endpoint'],  // OSS上传地址

        'policy' => $policyBase64,

        'signature' => $signature,

        'expire' => $expiration,

        'callback' => $base64CallbackBody,

        'dir' => $dir,

        'key' => $key,

        'url' => $bucket['host'] . DIRECTORY_SEPARATOR . $key  // 上传后的文件访问地址

    ];

}

2. 辅助方法实现

策略 Base64 编码:将上传策略转换为 Base64 字符串,用于生成签名:

php

/**

 * @desc 获取参数base64

 * @param $policyParams

 * @return string

 * @author Tinywan(ShaoBo Wan)

 */

private static function getPolicyBase64($policyParams): string

{

    return base64_encode(json_encode($policyParams));

}


生成签名:使用 OSS 访问密钥 Secret 对 Base64 编码的策略进行 HMAC-SHA1 加密,并对结果进行 Base64 编码:

php

/**

 * @desc 获取签名

 * @param string $policyBase64

 * @param string $accessKeySecret

 * @return string

 * @author Tinywan(ShaoBo Wan)

 */

private static function getSignature(string $policyBase64, string $accessKeySecret): string

{

    return base64_encode(hash_hmac('sha1', $policyBase64, $accessKeySecret, true));

}


计算过期时间:生成 UTC 格式的策略过期时间(当前时间 + 有效期):

php

/**

 * @desc 获取过期时间

 * @param int $time 有效期(秒)

 * @return string

 * @author Tinywan(ShaoBo Wan)

 */

private static function getExpireTime(int $time)

{

    return str_replace('+00:00', '.000Z', gmdate('c', time() + $time));

}


生成文件夹路径:按文件类型和月份分类存储,便于管理:

php

/**

 * @desc 获取按照月份分隔的文件夹路径

 * @param string $type 文件类型

 * @param string $directoryName 自定义目录名(如img/video)

 * @return string

 * @author Tinywan(ShaoBo Wan)

 */

private static function getDirectoryPath(string $type, string $directoryName): string

{

    if ($type === 'img') {

        return $directoryName . DIRECTORY_SEPARATOR . date('Y-m') . DIRECTORY_SEPARATOR;

    }

    return date('Y-m') . DIRECTORY_SEPARATOR;

}


生成随机文件名:使用 UUID 确保文件名唯一,避免冲突:

php

/**

 * @desc 获取一个随机的文件名

 * @param string $extend 文件扩展名(如jpg)

 * @return string

 * @author Tinywan(ShaoBo Wan)

 */

private static function getRandomFilename(string $extend): string

{

    return \Ramsey\Uuid\Uuid::uuid4()->toString() . '.' . $extend;

}


四、客户端实现:微信小程序上传文件

客户端(以微信小程序为例)需先向服务端请求上传参数,再使用wx.uploadFile接口将文件直传至 OSS。

1. 请求上传参数

客户端向服务端发送文件类型、目录名和扩展名,获取签名等参数:


请求参数:


json

{

  "type": "images",

  "dirname": "images",

  "ext": "png"

}



服务端响应:


json

{

  "code": 0,

  "msg": "success",

  "data": {

    "access_id": "xxxxxxxxxxxxxxxxx",

    "host": "https://tinywan-images.oss-cn-hangzhou.aliyuncs.com",

    "policy": "eyJlexxxxxxxxxxxxxxxxxxxx==",

    "signature": "YrlQxxxxxxxxxxxxxxxxxxxxxxtiI=",

    "expire": "2024-05-22T09:46:46.000Z",

    "callback": "eyJxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx0=",

    "dir": "ai/2024-05/",

    "key": "ai/2024-05/14b796b1-54ef-48d


Search Bar

最新资讯

2025-08-13

裸金属服务器:融合高性能与灵活...

2025-08-21

Linux服务器怎样更新防火墙...

2025-09-02

新加坡 VPS 云主机网络丢包...

2025-08-27

DNS 污染(DNS 劫持):...

2025-08-27

域名跳转实现指南:重定向与 D...