具有 Rails 个多态图像上传的 Shrine
Shrine with Rails multiple polymorphic image uploads
我已经苦苦挣扎了大约 5 个小时,试图理解为什么 Shrine 会阻止我的上传。我要么在强参数中得到像 "Shrine: Invalid file" 或 "Expected Array but got string" 这样的错误。如果没有错误,则图像实际上并未保存。
require "image_processing/mini_magick"
class ImageUploader < Shrine
include ImageProcessing::MiniMagick
plugin :activerecord
plugin :backgrounding
plugin :cached_attachment_data
plugin :determine_mime_type
plugin :delete_raw
plugin :direct_upload
plugin :logging, logger: Rails.logger
plugin :processing
plugin :remove_attachment
plugin :store_dimensions
plugin :validation_helpers
plugin :versions
Attacher.validate do
validate_max_size 2.megabytes, message: 'is too large (max is 2 MB)'
validate_mime_type_inclusion ['image/jpg', 'image/jpeg', 'image/png', 'image/gif']
end
def process(io, context)
case context[:phase]
when :store
thumb = resize_to_limit!(io.download, 200, 200)
{ original: io, thumb: thumb }
end
end
end
class Image < ActiveRecord::Base
include ImageUploader[:image]
belongs_to :imageable, polymorphic: true
end
class Product < ApplicationRecord
has_many :images, as: :imageable, dependent: :destroy
accepts_nested_attributes_for :images, allow_destroy: true
...
# Strong Params:
def product_params
params.require(:product).permit(
:name, :brand_id, :category_id, :price, :compare_price, :description,
images_attributes: { image: [] },
product_properties_attributes: [:id, :property_id, :value]
)
...
而我的观点:
<%= f.fields_for :images do |image_form| %>
<%= image_form.file_field :image, multiple: true %>
<% end %>
根据我在文档或 gorails 上阅读的所有内容,这应该可行。我需要重组 images_attributes
哈希吗?我也尝试使用 direct_uploads,但很难让 presigned_url 与 S3 一起工作。
Refile 使这变得非常简单,所以我可能会 运行 哭回那个。
我明显做错了什么吗?
根据 fields_for
documentation,将为 project.images
集合中的每个图像调用提供的块。因此,如果您的产品当前没有任何图像,则不会调用该块(根据文档)。
要使嵌套属性起作用,您需要在创建产品时转发以下参数:
product[images_attributes][0][image] = <file object or data hash>
product[images_attributes][1][image] = <file object or data hash>
product[images_attributes][2][image] = <file object or data hash>
...
如果您查看“Multiple Files”神社指南,建议您只有一个接受多个文件的文件字段:
<input type="file" name="file" multiple>
然后使用 Uppy 为该字段设置直接上传,为每个填充有上传文件数据哈希的上传文件动态生成 image
字段:
<input type="hidden" name="product[images_attributes][0][image]" value='{"id":"...","storage":"cache","metadata":{...}}'>
<input type="hidden" name="product[images_attributes][1][image]" value='{"id":"...","storage":"cache","metadata":{...}}'>
....
或者你可以让用户附加多个文件,这些文件都提交给应用程序,然后在控制器中解构它们:
class ProductsController < ApplicationController
def create
images_attributes = params["files"].map { |file| {image: file} }
Product.create(product_params.merge(images_attributes: images_attributes))
end
end
在这种情况下,您必须确保您的 HTML 表单设置了 enctype="multipart/form-data"
属性(否则只会提交文件的文件名,而不是文件本身)。
我已经苦苦挣扎了大约 5 个小时,试图理解为什么 Shrine 会阻止我的上传。我要么在强参数中得到像 "Shrine: Invalid file" 或 "Expected Array but got string" 这样的错误。如果没有错误,则图像实际上并未保存。
require "image_processing/mini_magick"
class ImageUploader < Shrine
include ImageProcessing::MiniMagick
plugin :activerecord
plugin :backgrounding
plugin :cached_attachment_data
plugin :determine_mime_type
plugin :delete_raw
plugin :direct_upload
plugin :logging, logger: Rails.logger
plugin :processing
plugin :remove_attachment
plugin :store_dimensions
plugin :validation_helpers
plugin :versions
Attacher.validate do
validate_max_size 2.megabytes, message: 'is too large (max is 2 MB)'
validate_mime_type_inclusion ['image/jpg', 'image/jpeg', 'image/png', 'image/gif']
end
def process(io, context)
case context[:phase]
when :store
thumb = resize_to_limit!(io.download, 200, 200)
{ original: io, thumb: thumb }
end
end
end
class Image < ActiveRecord::Base
include ImageUploader[:image]
belongs_to :imageable, polymorphic: true
end
class Product < ApplicationRecord
has_many :images, as: :imageable, dependent: :destroy
accepts_nested_attributes_for :images, allow_destroy: true
...
# Strong Params:
def product_params
params.require(:product).permit(
:name, :brand_id, :category_id, :price, :compare_price, :description,
images_attributes: { image: [] },
product_properties_attributes: [:id, :property_id, :value]
)
...
而我的观点:
<%= f.fields_for :images do |image_form| %>
<%= image_form.file_field :image, multiple: true %>
<% end %>
根据我在文档或 gorails 上阅读的所有内容,这应该可行。我需要重组 images_attributes
哈希吗?我也尝试使用 direct_uploads,但很难让 presigned_url 与 S3 一起工作。
Refile 使这变得非常简单,所以我可能会 运行 哭回那个。
我明显做错了什么吗?
根据 fields_for
documentation,将为 project.images
集合中的每个图像调用提供的块。因此,如果您的产品当前没有任何图像,则不会调用该块(根据文档)。
要使嵌套属性起作用,您需要在创建产品时转发以下参数:
product[images_attributes][0][image] = <file object or data hash>
product[images_attributes][1][image] = <file object or data hash>
product[images_attributes][2][image] = <file object or data hash>
...
如果您查看“Multiple Files”神社指南,建议您只有一个接受多个文件的文件字段:
<input type="file" name="file" multiple>
然后使用 Uppy 为该字段设置直接上传,为每个填充有上传文件数据哈希的上传文件动态生成 image
字段:
<input type="hidden" name="product[images_attributes][0][image]" value='{"id":"...","storage":"cache","metadata":{...}}'>
<input type="hidden" name="product[images_attributes][1][image]" value='{"id":"...","storage":"cache","metadata":{...}}'>
....
或者你可以让用户附加多个文件,这些文件都提交给应用程序,然后在控制器中解构它们:
class ProductsController < ApplicationController
def create
images_attributes = params["files"].map { |file| {image: file} }
Product.create(product_params.merge(images_attributes: images_attributes))
end
end
在这种情况下,您必须确保您的 HTML 表单设置了 enctype="multipart/form-data"
属性(否则只会提交文件的文件名,而不是文件本身)。