首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >PHP+腾讯云 COS 图片上传教程

PHP+腾讯云 COS 图片上传教程

原创
作者头像
岳泽以
发布2026-03-26 21:19:05
发布2026-03-26 21:19:05
1350
举报
文章被收录于专栏:PHPPHP

腾讯云 COS 图片上传教程

准备工作

1. 安装腾讯云 COS SDK

代码语言:bash
复制
composer require qcloud/cos-sdk-v5

2. 配置环境变量

在项目根目录的 .env 文件中添加以下配置:

代码语言:env
复制
# 腾讯云 COS 配置
COS_REGION=ap-guangzhou          # COS存储桶所在地域
COS_BUCKET=your-bucket-name      # 存储桶名称(格式:name-appid)
COS_SECRET_ID=your-secret-id     # 云API密钥SecretId
COS_SECRET_KEY=your-secret-key   # 云API密钥SecretKey
COS_DOMAIN=https://your-domain.com # 自定义域名(可选,使用默认域名可省略)
COS_USE_IMAGE_MORPH=false        # 是否使用数据万象生成缩略图

3. 获取腾讯云密钥

  1. 访问 腾讯云控制台
  2. 进入「访问管理」→「API密钥管理」
  3. 创建或获取 SecretIdSecretKey

代码实现

创建 COSService 服务类

代码语言:php
复制
<?php

namespace App\Services;

use Qcloud\Cos\Client;

class COSService
{
    private $cosClient;

    public function __construct()
    {
        // 解析自定义域名
        $customDomain = $_ENV['COS_DOMAIN'];
        $domain = parse_url($customDomain, PHP_URL_HOST) 
                ?: str_replace(['https://', 'http://'], '', $customDomain);
        $domain = rtrim($domain, '/');

        // 初始化 COS 客户端
        $this->cosClient = new Client([
            'region' => $_ENV['COS_REGION'],
            'credentials' => [
                'secretId' => $_ENV['COS_SECRET_ID'],
                'secretKey' => $_ENV['COS_SECRET_KEY']
            ],
            'schema' => 'https',
            'domain' => $domain,
            'timeout' => 60,
            'connect_timeout' => 60,
            'proxy' => null
        ]);

        // 禁用SSL验证(仅开发环境使用,生产环境应开启)
        $httpClient = $this->cosClient->httpClient;
        $config = $httpClient->getConfig();
        $config['verify'] = false;
        
        $reflection = new \ReflectionClass($httpClient);
        $configProperty = $reflection->getProperty('config');
        $configProperty->setAccessible(true);
        $configProperty->setValue($httpClient, $config);
    }

    /**
     * 上传图片到COS
     * @param \Psr\Http\Message\UploadedFileInterface $uploadedFile 上传的文件
     * @param string $type 文件类型:content(内容)、category(分类)、avatar(头像)
     * @return array 返回上传结果
     */
    public function uploadImage($uploadedFile, string $type = 'content'): array
    {
        $bucket = $_ENV['COS_BUCKET'];
        $extension = pathinfo($uploadedFile->getClientFilename(), PATHINFO_EXTENSION);
        $filename = uniqid('img_', true) . '.' . $extension;

        // 根据类型确定存储路径
        if ($type === 'category') {
            $key = 'categories/' . $filename;
        } elseif ($type === 'avatar') {
            $key = 'avatars/' . $filename;
        } else {
            // 内容图片:按日期/小时分组存储(如:2026011522/img_xxx.jpg)
            $datePath = date('YmdH');
            $key = $datePath . '/' . $filename;
        }

        // 上传到COS
        $result = $this->cosClient->putObject([
            'Bucket' => $bucket,
            'Key' => $key,
            'Body' => $uploadedFile->getStream()->getContents(),
            'ContentType' => $uploadedFile->getClientMediaType()
        ]);

        // 生成访问URL
        $cosUrl = rtrim($_ENV['COS_DOMAIN'], '/') . '/' . $key;

        // 可选:生成缩略图URL(使用数据万象)
        $useImageMorph = filter_var($_ENV['COS_USE_IMAGE_MORPH'] ?? 'false', FILTER_VALIDATE_BOOLEAN);
        $thumbnailUrl = $useImageMorph 
            ? $cosUrl . '?imageMogr2/thumbnail/400x300/quality/80' 
            : $cosUrl;

        // 获取文件大小
        $size = $uploadedFile->getSize();

        return [
            'cos_url' => $cosUrl,
            'thumbnail_url' => $thumbnailUrl,
            'size' => $size,
            'format' => $extension
        ];
    }

    /**
     * 删除COS中的文件
     * @param string $key 文件在COS中的键名
     * @return bool 是否删除成功
     */
    public function deleteFile(string $key): bool
    {
        try {
            $bucket = $_ENV['COS_BUCKET'];
            $this->cosClient->deleteObject([
                'Bucket' => $bucket,
                'Key' => $key
            ]);
            return true;
        } catch (\Exception $e) {
            return false;
        }
    }
}

使用示例

在控制器中使用

代码语言:php
复制
<?php

namespace App\Controllers;

use App\Services\COSService;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;

class ImageController
{
    private $cosService;

    public function __construct(COSService $cosService)
    {
        $this->cosService = $cosService;
    }

    /**
     * 上传图片
     */
    public function upload(Request $request, Response $response): Response
    {
        $uploadedFile = $request->getUploadedFiles()['file'] ?? null;
        
        if (!$uploadedFile || $uploadedFile->getError() !== UPLOAD_ERR_OK) {
            $response->getBody()->write(json_encode([
                'success' => false,
                'message' => '文件上传失败'
            ]));
            return $response->withHeader('Content-Type', 'application/json');
        }

        // 上传到COS(type默认为content)
        $result = $this->cosService->uploadImage($uploadedFile, 'content');

        $response->getBody()->write(json_encode([
            'success' => true,
            'data' => $result
        ]));
        
        return $response->withHeader('Content-Type', 'application/json');
    }

    /**
     * 上传分类图片
     */
    public function uploadCategory(Request $request, Response $response): Response
    {
        $uploadedFile = $request->getUploadedFiles()['file'] ?? null;
        
        if (!$uploadedFile || $uploadedFile->getError() !== UPLOAD_ERR_OK) {
            $response->getBody()->write(json_encode([
                'success' => false,
                'message' => '文件上传失败'
            ]));
            return $response->withHeader('Content-Type', 'application/json');
        }

        // 上传到COS的categories目录
        $result = $this->cosService->uploadImage($uploadedFile, 'category');

        $response->getBody()->write(json_encode([
            'success' => true,
            'data' => $result
        ]));
        
        return $response->withHeader('Content-Type', 'application/json');
    }

    /**
     * 上传用户头像
     */
    public function uploadAvatar(Request $request, Response $response): Response
    {
        $uploadedFile = $request->getUploadedFiles()['file'] ?? null;
        
        if (!$uploadedFile || $uploadedFile->getError() !== UPLOAD_ERR_OK) {
            $response->getBody()->write(json_encode([
                'success' => false,
                'message' => '文件上传失败'
            ]));
            return $response->withHeader('Content-Type', 'application/json');
        }

        // 上传到COS的avatars目录
        $result = $this->cosService->uploadImage($uploadedFile, 'avatar');

        $response->getBody()->write(json_encode([
            'success' => true,
            'data' => $result
        ]));
        
        return $response->withHeader('Content-Type', 'application/json');
    }

    /**
     * 删除图片
     */
    public function delete(Request $request, Response $response): Response
    {
        $data = json_decode($request->getBody()->getContents(), true);
        $key = $data['key'] ?? '';

        if (empty($key)) {
            $response->getBody()->write(json_encode([
                'success' => false,
                'message' => '文件key不能为空'
            ]));
            return $response->withHeader('Content-Type', 'application/json');
        }

        $success = $this->cosService->deleteFile($key);

        $response->getBody()->write(json_encode([
            'success' => $success,
            'message' => $success ? '删除成功' : '删除失败'
        ]));
        
        return $response->withHeader('Content-Type', 'application/json');
    }
}

前端调用示例

使用 FormData 上传

代码语言:javascript
复制
async function uploadImage(file) {
    const formData = new FormData();
    formData.append('file', file);

    try {
        const response = await fetch('/api/upload', {
            method: 'POST',
            body: formData
        });

        const result = await response.json();
        
        if (result.success) {
            console.log('上传成功:', result.data);
            // result.data.cos_url - 原图URL
            // result.data.thumbnail_url - 缩略图URL
            // result.data.size - 文件大小
            // result.data.format - 文件格式
            return result.data;
        } else {
            console.error('上传失败:', result.message);
        }
    } catch (error) {
        console.error('上传出错:', error);
    }
}

// 使用示例
const fileInput = document.getElementById('file-input');
fileInput.addEventListener('change', async (e) => {
    const file = e.target.files[0];
    if (file) {
        await uploadImage(file);
    }
});

存储路径说明

  • content: 按日期+小时分组,路径格式:2026011522/img_xxx.jpg
  • category: 存储在 categories/ 目录下
  • avatar: 存储在 avatars/ 目录下

注意事项

  1. 环境安全: 生产环境请勿将 .env 文件提交到版本控制系统
  2. SSL验证: 示例中禁用了SSL验证仅用于开发环境,生产环境建议开启
  3. 密钥安全: 妥善保管 SecretIdSecretKey,避免泄露
  4. 权限设置: 在COS控制台为存储桶设置合适的访问权限
  5. 自定义域名: 如需使用自定义域名,需在COS控制台配置域名解析
  6. 数据万象: 如需使用缩略图功能,需要在COS控制台开通数据万象服务

常见问题

Q: 上传时提示权限错误?

A: 检查SecretId和SecretKey是否正确,以及存储桶是否授予了相关权限。

Q: 如何使用COS默认域名?

A: 删除 COS_DOMAIN 配置或将其设置为COS提供的默认域名。

Q: 如何限制上传文件大小?

A: 在前端和后端都应添加文件大小校验,例如限制最大10MB。

Q: 支持哪些图片格式?

A: 支持常见的图片格式,如jpg、jpeg、png、gif、webp等。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 腾讯云 COS 图片上传教程
    • 准备工作
      • 1. 安装腾讯云 COS SDK
      • 2. 配置环境变量
      • 3. 获取腾讯云密钥
    • 代码实现
      • 创建 COSService 服务类
    • 使用示例
      • 在控制器中使用
    • 前端调用示例
      • 使用 FormData 上传
    • 存储路径说明
    • 注意事项
    • 常见问题
      • Q: 上传时提示权限错误?
      • Q: 如何使用COS默认域名?
      • Q: 如何限制上传文件大小?
      • Q: 支持哪些图片格式?
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档