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

Ruby on Rails ActiveStorage:image_tag无法显图但img用URL可行

ActiveStorage + S3 下image_tag代理路由404问题解决

问题现象

使用Ruby on Rails ActiveStorage结合AWS S3存储文件时,仅直接通过person.as_photo.url生成的预签名URL能正常显示图片,image_tag(person.as_photo)和rails_storage_proxy_path生成的代理/重定向URL均返回404错误。

配置与测试代码

1. storage.yml配置

# Use rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key)
amazon:
  service: S3
  # Instead of specifying these or using Rails.application.credentials we can rely on env vars or any other valid way of authenticating the aws sdk gem
  access_key_id:     # 测试用硬编码密钥
  secret_access_key: # 测试用硬编码密钥
  region: us-east-1
  bucket: test-my-s3-bucket-name

2. Personnel模型代码

class Personnel < ApplicationRecord
  ...
  # NEW ACTIVESTORAGE CODE
  has_one_attached :as_photo
  # OPTIONAL:
  validates :as_photo, size: { less_than: 3.megabytes , message: 'is too large' },
                       content_type: {in: ['image/jpeg', 'image/png', 'image/gif', 'image/pjpeg', 'image/x-png'],
                                      message: I18n.t("errors.file_invalid_format", :formats => "JPG,GIF,PNG") }

  before_save :define_attachment_key
  # 基于模型名生成文件key,用于在S3中创建"子文件夹"分类存储
  def photo_path
    # 确保key全局唯一,使用ActiveStorage的安全token生成方法
    "public/#{Rails.env}/models/#{self.class.name}/photo/#{ActiveStorage::Blob.generate_unique_secure_token}"
  end

  def define_attachment_key
    as_photo.key = photo_path if as_photo.new_record?
    # 多附件场景可在此添加其他..._path方法,无自定义路径的附件会存在桶根目录
  end
end

3. 测试模板代码

<h1>Personnel</h1>
<h2>There are a total of <%= @count %> Personnel records</h2>

<ul>
  <% @persons.each do |person| %>
    <li>
      <%= person.id %>
      <% if person.as_photo.present? %>
        <ul>
          <li>
              <%= image_tag(person.as_photo) %>
          </li>
          <li>
            Proxy: <%= image_tag rails_storage_proxy_path(person.as_photo) %>
          </li>
          <li>
            img tag with as.url: <img src="<%= person.as_photo.url %>" alt="an image wow">
          </li>
        </ul>
      <% end %>
    </li>
  <% end %>
</ul>

问题核心原因

直接调用url生成的是AWS S3预签名URL,绕过了Rails代理;而image_tag(attachment)和rails_storage_proxy_path依赖Rails的ActiveStorage路由代理请求,404的根源是自定义Blob key格式干扰了ActiveStorage的内部路径解析逻辑。

ActiveStorage默认维护Blob的key与存储路径的映射关系,你手动设置的public/...格式key,会导致代理路由尝试查找文件时,无法匹配到S3上的实际文件路径,最终返回404。

解决方案

方案1:移除自定义key设置(推荐)

ActiveStorage默认会自动生成唯一key和合理的存储结构,若仅需按模型分类存储,可通过S3配置的prefix参数实现,无需手动修改Blob的key:

修改storage.yml的amazon配置,添加统一前缀:

amazon:
  service: S3
  access_key_id:     # 你的密钥
  secret_access_key: # 你的密钥
  region: us-east-1
  bucket: test-my-s3-bucket-name
  prefix: "public/#{Rails.env}/models" # 所有模型的文件都会存储在此前缀路径下

随后移除模型中的before_save :define_attachment_key和photo_path方法,让ActiveStorage自动管理key。

方案2:使用直接服务URL替代代理路由

若无需Rails代理(比如文件允许公开访问),可将S3存储桶配置为公开读写,然后使用person.as_photo.service_url生成永久URL,此时image_tag(person.as_photo)会自动使用该URL,无需经过Rails代理。

注意:公开存储桶需做好权限控制,避免未授权访问。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 16:34:59