composer require qcloud/cos-sdk-v5在项目根目录的 .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 # 是否使用数据万象生成缩略图SecretId 和 SecretKey<?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
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');
}
}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);
}
});2026011522/img_xxx.jpgcategories/ 目录下avatars/ 目录下.env 文件提交到版本控制系统SecretId 和 SecretKey,避免泄露A: 检查SecretId和SecretKey是否正确,以及存储桶是否授予了相关权限。
A: 删除 COS_DOMAIN 配置或将其设置为COS提供的默认域名。
A: 在前端和后端都应添加文件大小校验,例如限制最大10MB。
A: 支持常见的图片格式,如jpg、jpeg、png、gif、webp等。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。