Dan*_*Dan 8 ruby ruby-on-rails rails-activestorage ruby-on-rails-6
这个问题已经持续了很长一段时间了,直到今天,我仍然没有找到解决方案。我见过一些类似的问题,但不完全是我所经历的。
自从 Rails Active Storage 在 Rails 5 中推出以来,我一直在体验它,但由于这个特殊问题,我从未在生产中实际使用它。Rails 6 发布时解决的主要问题是,如果您对文件附件实施任何验证,记录将不会保存,但附件(blob)仍会被保存,并且如果您的验证是针对内容类型(例如,确保它是 JPEG 图像),然后您最终会得到无效的附件(例如,如果您上传了文本文件)。例如,如果您尝试使用 image_tag“显示”此附件,这可能会导致问题。Rails 6 通过仅在记录实际保存到数据库中时才保存附件来解决此问题。这对我来说只能解决一半的问题。
这是我仍然遇到的情况,尚未找到解决方案。
假设您有一个非常基本的设置。具有姓名、电子邮件和附加头像的 Person 模型
Class Person < ApplicationRecord
has_one_attached :avatar
#Check that image type is jpg or png
validate :check_image_type
#Remove avatar flag needed for form
attr_accessor :remove_avatar
#purge picture if remove picture flag was ticked on form
after_save :purge_avatar, if: :purge_requested?
#Returns a thumbnail version of the property picture
def thumbnail
return self.avatar.variant(resize:'100x100').processed
end
private
#Validates the image type being uploaded
def check_image_type
if avatar.attached? && !avatar.content_type.in?(%("image/jpeg image/png"))
errors.add(:avatar, "Invalid Avatar Format")
end
end
#Was a purge of the picture requested
def purge_requested?
remove_avatar == "1"
end
def purge_avatar
avatar.purge_later
end
end
Run Code Online (Sandbox Code Playgroud)
正如您在上面的代码中看到的,该人有一个附加的头像。保存后,我们验证图像类型,确保它是 jpep 或 png,如果不是,我们只需向记录添加错误,这将阻止保存记录。
这是控制器代码(我省略了索引、编辑、更新和销毁操作)
class PeopleController < ApplicationController
before_action :set_person, only: [:show, :edit, :update, :destroy]
def new
@person = Person.new
end
def create
@person = Person.new(person_params)
respond_to do |format|
if @person.save
format.html { redirect_to @person, notice: 'Person was successfully created.' }
format.json { render :show, status: :created, location: @person }
else
format.html { render :new }
format.json { render json: @person.errors, status: :unprocessable_entity }
end
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_person
@person = Person.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def person_params
params.require(:person).permit(:name, :email, :avatar, :remove_avatar)
end
end
Run Code Online (Sandbox Code Playgroud)
然后在表单中,如果头像存在于表单顶部,我会显示该头像,然后让用户创建/编辑信息并根据需要选择另一个头像。
<%= form_with(model: person, local: true) do |form| %>
<% if person.errors.any? %>
<div id="error_explanation">
<h2>
<%= pluralize(person.errors.count, "error") %> prohibited this person from being saved:
</h2>
<ul>
<% person.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<!-- Display avatar if one attach -->
<%if person.avatar.attached?%>
<%=image_tag(person.avatar)%>
Remove <%=form.check_box :remove_avatar%>
<%end%>
<div class="field">
<%= form.label :name %>
<%= form.text_field :name %>
</div>
<div class="field">
<%= form.label :email %>
<%= form.text_field :email %>
</div>
<!-- Select Picture -->
<div class = "field">
<%=form.label :avatar %>
<%= form.file_field :avatar, accept: 'image/*'%>
</div>
<div class="actions">
<%= form.submit %>
</div>
<% end %>
Run Code Online (Sandbox Code Playgroud)
预期的行为是
发生的情况是,当您选择错误的文件类型时,它确实会阻止保存记录,这很好,但它仍然“看到”非持久记录上的附件。因此,尽管在创建操作中最初显示表单时没有显示头像,但在验证失败后重新呈现表单时,会显示“空”头像。这是由于附件吗?即使记录上没有附件(因为它被拒绝),方法也会返回 true。
空白头像看起来有点时髦,看起来就像一张链接断开的图片。但是,如果您要对头像本身进行任何操作,例如 Person 类中的缩略图方法,则会生成以下错误:ActiveStorage::InvariableError
这是由于附件吗?属性为 true 并且头像属性有效,但没有关联的 blob(图片)。因此,尝试将其大小调整为缩略图会失败并出现错误。
我正在尝试找到一种方法来“清除”或重置头像和/或附加的?当验证阻止记录保存时的属性。在内部,Rails 执行它应该执行的操作(不保存文件并保留当前文件(如果有))。
但是表单上显示的内容(如果您显示头像)必然会让用户感到困惑,特别是如果用户在您选择新头像之前已有头像。如果您选择了无效的头像并且表单拒绝了它,则在重新渲染时,您的初始头像不会显示,而是会替换为图片损坏的链接图标。这可能会让用户误以为他们之前的头像已被删除,但事实并非如此。目前,我不确定如何在不深入活动存储内部的情况下解决这个问题(我现在对此不感兴趣)。
我尝试自己调用 purge 或将 null 分配给 avatar 属性,但这不起作用
def check_image_type
if avatar.attached? && !avatar.content_type.in?(%("image/jpeg image/png"))
errors.add(:avatar, "Invalid Avatar Format")
avatar.purge <---- Not working
avatar = nil <--- Not working
avatar = '' <--- Not working
end
end
Run Code Online (Sandbox Code Playgroud)
编辑:我不一定要尝试显示我刚刚上传的内容的预览。事实上,我不想这样做,但 Rails 似乎自己在这样做。
在我的示例中,当您创建用户时,没有头像,因此没有显示任何头像,但是当您尝试上传错误文件类型的头像并且表单重新加载以显示错误时,它会尝试显示加载失败的头像。如果上传的文件类型正确,它会保存用户信息并重定向到用户列表或另一个屏幕,我们可以在其中显示加载的头像。
当用户已有头像而您想要更改它时。您首先打开表单(使用编辑操作),它会显示当前的头像。如果我尝试更改它并再次上传无效文件,表单将重新加载并出现错误,但再次将当前有效的头像替换为空头像(即使我是数据库,旧的头像仍然存在)。同样,如果我上传有效文件,则表单将提交,头像将更改,我们将在下一个屏幕中看到它。
总而言之,(我认为)正确的行为应该是,如果我尝试上传一个基于验证(文件类型、大小等)而被拒绝的文件,那么 Rails 应该表现得好像我什至没有尝试过上传一个文件一样。文件。它应该废弃“暂定附件”的任何残留物。
对于新资源,它仍然不会显示头像,但对于已经存在的资源,它仍然会显示当前头像。
mec*_*cov 10
为了防止错误,您可以使用persisted?
<% if person.avatar.attached? && person.avatar.persisted? %>
<%= image_tag(person.avatar)%>
Remove <%= form.check_box :remove_avatar%>
<%end%>
Run Code Online (Sandbox Code Playgroud)
您可以使用此 gem进行 ActiveStorage 验证。
像这样:
validates :avatar, content_type: %w[image/png image/jpg image/jpeg]
Run Code Online (Sandbox Code Playgroud)