You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Symfony5.4下AWS S3+Flysystem+LiipImagine预签名URL及403权限问题

问题:Symfony 5.4 + AWS S3 + Flysystem + LiipImagine 私有桶缩略图权限与预签名URL失效问题

环境与配置

IAM权限配置

"Effect": "Allow",
"Action": [
    "s3:ListBucket",
    "s3:GetObject",
    "s3:DeleteObject",
    "s3:GetObjectAcl",
    "s3:PutObjectAcl",
    "s3:PutObject"
],
"Resource": [
    "arn:aws:s3:::myBucket",
    "arn:aws:s3:::mybucket/*"
]

Symfony配置文件

service.yaml

...
parameters:
    uploads_base_url: '%env(AWS_S3_UPLOAD_BASE_URL)%'   

services : 
    Aws\S3\S3Client:
        arguments:
            -
                version: '2006-03-01'
                region: '%env(AWS_S3_ACCESS_REGION)%' 
                credentials:
                    key: '%env(AWS_S3_ACCESS_ID)%'
                    secret: '%env(AWS_S3_ACCESS_SECRET)%'

oneup_flysystem.yaml

...
adapters:
    aws_s3_adapter:                    
        awss3v3:
            client : Aws\S3\S3Client
            bucket: '%env(AWS_S3_BUCKET_NAME)%'
            options:
                ACL: bucket-owner-full-control

filesystems:
    aws_s3_system:
        adapter: aws_s3_adapter

liip_imagine.yaml

...
driver: "gd"

loaders:
    aws_s3_loader:  
        flysystem:
            filesystem_service: oneup_flysystem.aws_s3_system_filesystem

data_loader: aws_s3_loader

resolvers:
    aws_s3_resolver:
        flysystem:
            filesystem_service: oneup_flysystem.aws_s3_system_filesystem
            root_url: '%uploads_base_url%'
            cache_prefix: media/cache
     
cache: aws_s3_resolver 

filter_sets:
    squared_thumbnail_small:
        quality: 70
        filters:
            thumbnail:
                size:          [50, 50]
                mode:          outbound

Twig与服务代码

Twig模板调用

<a href="{{ assetPresigned('photos', player.photoFilename) }}" target="_blank">
    <img src="{{ assetPresigned('photos', player.photoFilename ) | imagine_filter('squared_thumbnail_small') }}" alt="photo">
</a>

FileUploadService.php 原方法

public function assetPresigned(string $folder, string $filename): string
{
    $command = $this->s3Client->getCommand('GetObject', [
        'Bucket' => $this->awsS3BucketName,
        'Key'    => $folder.'/'.$filename,
    ]);

    $urlPresigned = $this->s3Client->createPresignedRequest($command, '+5 minutes');
    return ((string) $urlPresigned->getUri());
}

核心问题

  1. imagine_filter会重写预签名URL,移除签名信息,导致img标签无法访问私有桶中的缩略图
  2. 手动触发缩略图生成时,存储到S3返回403 Forbidden错误,推测权限或凭证配置异常

解决方案

步骤1:修复IAM权限大小写问题

S3的ARN严格区分大小写,将IAM配置中的Resource路径改为与Bucket名完全一致:

"Resource": [
    "arn:aws:s3:::myBucket",
    "arn:aws:s3:::myBucket/*"
]

步骤2:修改服务代码,支持缩略图预签名URL生成

重构FileUploadService,利用LiipImagine的CacheManager自动处理缩略图生成,同时对缩略图路径生成预签名:

namespace App\Service;

use Aws\S3\S3Client;
use Liip\ImagineBundle\Imagine\Cache\CacheManager;

class FileUploadService
{
    private $s3Client;
    private $awsS3BucketName;
    private $cacheManager;

    public function __construct(S3Client $s3Client, string $awsS3BucketName, CacheManager $cacheManager)
    {
        $this->s3Client = $s3Client;
        $this->awsS3BucketName = $awsS3BucketName;
        $this->cacheManager = $cacheManager;
    }

    // 原方法:生成原文件预签名URL
    public function assetPresigned(string $folder, string $filename): string
    {
        $command = $this->s3Client->getCommand('GetObject', [
            'Bucket' => $this->awsS3BucketName,
            'Key'    => $folder.'/'.$filename,
        ]);

        $urlPresigned = $this->s3Client->createPresignedRequest($command, '+5 minutes');
        return (string) $urlPresigned->getUri();
    }

    // 新增方法:生成缩略图预签名URL
    public function thumbnailPresigned(string $folder, string $filename, string $filter): string
    {
        $originalPath = $folder.'/'.$filename;
        
        // 自动触发缩略图生成与缓存
        if (!$this->cacheManager->isStored($originalPath, $filter)) {
            $this->cacheManager->resolve($originalPath, $filter);
        }

        // 获取缩略图在S3中的存储Key
        $thumbnailBrowserPath = $this->cacheManager->getBrowserPath($originalPath, $filter);
        $thumbnailKey = ltrim(str_replace($_ENV['AWS_S3_UPLOAD_BASE_URL'], '', $thumbnailBrowserPath), '/');

        // 生成缩略图预签名URL
        $command = $this->s3Client->getCommand('GetObject', [
            'Bucket' => $this->awsS3BucketName,
            'Key'    => $thumbnailKey,
        ]);

        $urlPresigned = $this->s3Client->createPresignedRequest($command, '+5 minutes');
        return (string) $urlPresigned->getUri();
    }
}

步骤3:修改Twig调用逻辑

直接调用新增的thumbnailPresigned方法,避免对预签名URL应用过滤器:

<a href="{{ assetPresigned('photos', player.photoFilename) }}" target="_blank">
    <img src="{{ thumbnailPresigned('photos', player.photoFilename, 'squared_thumbnail_small') }}" alt="photo">
</a>

关键说明

  1. IAM权限的大小写不匹配是403错误的核心原因,必须保证Resource路径与Bucket名完全一致
  2. 原逻辑中对预签名URL应用imagine_filter是错误的,过滤器会生成公开缓存URL,正确做法是对缩略图的存储路径单独生成预签名
  3. 利用LiipImagine的CacheManager自动处理缩略图生成与存储,无需手动调用底层的DataManager和FilterManager,减少代码复杂度

内容的提问来源于stack exchange,提问作者brico

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 17:06:57