如何同时上传多张图片和更新一个实体。 Rails API 和 JS 前端

How to upload multiple images and update an entity at the same time. Rails API and JS frontend

我正在尝试使用来自 React 前端的 formData 进行一次 API 调用,该前端具有多个图像加上 strings/booleans 等以更新实体

我正在使用活动存储并且我能够将多个图像附加到实体但是当我想用字符串等更新实体时通过一个 API 调用(理想情况下)。它是这样说的:

param is missing or the value is empty: property

我知道原因是我的formData结构是这样的:

data: {"title": "xxxxxx", "description": "xxxxxx" etc.}, images[]: File, images[]: File etc.

而且我需要 属性 作为键而不是数据:

def property_params
    params.require(:property).permit(:title, :description, :max_guests, :price_per_day, :address,:average_rating, :has_beach_nearby, :has_beds, :has_kitchen, :has_swimming_pool, :has_hdtv,:has_bathtub, :images [])
end

但是当我不这样做时,图像无法识别,因为它是不允许的,所以 API 没有收到它

我正在附加这样的图像(并且有效):

if params[:images].present?
    params[:images].each do |image|
        @property.images.attach(image)
    end
end

是否可以使用一次 API 调用上传多张图片和数据?如果没有,我需要做什么?

这是整个 属性 控制器:

class Api::V1::PropertiesController < ApiController
    before_action :authenticate_user!, only: [:update, :create, :destroy]
            
    def index
       @properties = Property.all
       render json: @properties
    end
            
    def show
       @property = Property.find(params[:id])
       render json: @property
    end
            
    def update
      @property = Property.find(params[:id])
            
      if current_user === @property.user
        if params[:images].present?
           params[:images].each do |image|
              @property.images.attach(image)
           end
        end
            
        @property.update(property_params)
                    
        render json: {
           success: "Successfully updated",property: @property},
           status: 200
      else
        render json: {
           error: "You are not authorized to update this property"}, 
           status: 403
      end
    end
                
    private
    def property_params
       params.require(:property).permit(:title, :description, :max_guests, :price_per_day, :address,:average_rating, :has_beach_nearby, :has_beds, :has_kitchen, :has_swimming_pool, :has_hdtv,:has_bathtub, :images [])
    end

被卡住了一段时间,非常感谢任何帮助(Rails 的新手)

Rack 使用括号在表单数据中传递哈希:

irb(main):005:0> Rack::Utils.parse_nested_query("property[title]=foo&property[description]=bar")
=> {"property"=>{"title"=>"foo", "description"=>"bar"}}

可以使用空括号传递数组:

irb(main):008:0> Rack::Utils.parse_nested_query("property[foo][]=a&property[foo]&[]=property[foo][]=c")
=> {"property"=>{"foo"=>nil}}

对于多部分表单数据,您通常会使用表单(可见表单或虚拟表单)并在输入上设置名称属性:

<form action="/properties" method="post" multipart>
  <input name="property[images][]" type="file" />
  <input name="property[images][]" type="file" />
</form>

Rack 会将请求正文的各个部分拼接在一起并创建单个参数哈希。

你也有语法错误:

# Use sensible line lengths and this won't happen as often...
params.require(:property)
      .permit(
         :title, :description, :max_guests, 
         :price_per_day, :address, :average_rating, 
         :has_beach_nearby, :has_beds, :has_kitchen, 
         :has_swimming_pool, :has_hdtv, :has_bathtub, 
         images: []
      )

要将数组或嵌套参数列入白名单,您需要使用哈希作为允许的最后一个参数。 images: [] 将允许包含 ActionDispatch::UploadedFile.

的允许标量数组

如果您想将包含嵌套参数和平面参数的非常规参数列入白名单,您需要更有创意。要记住的关键是 ActionController::Parameters 是一个类似散列的对象,您可以像散列一样操作它:

def property_params   
  params.require(:data)
        .permit(
           :title, :description, :max_guests, 
           :price_per_day, :address, :average_rating, 
           :has_beach_nearby, :has_beds, :has_kitchen, 
           :has_swimming_pool, :has_hdtv, :has_bathtub
        ).merge(
           # fetch is like require but does not raise if the key is not present
           images: params.fetch(:images, []) 
        )
end

使用 ActiveStorage,您实际上 need to manually attach the files

我好像这样解决了:

    def update
        @property = Property.find(params[:id])
        if current_user === @property.user   
            if params[:images]
                @property.images.attach(params[:images])
            end

            property = @property.update(property_params)

            if property 
                render json: {
                    success: "Successfully updated",
                    property: @property,
                    images: @property.images
                }, status: 200
            else
                render json: {
                    error: "Error when updating this property",
                }, status: 500
            end
        else
            render json: {
                error: "You are not authorized to update this property"
            }, status: 403
        end
    end

    private

    def property_params
        json = JSON.parse(params[:data])
        json = ActionController::Parameters.new(json)
        json.permit(
                :title, :description, :max_guests, 
                :price_per_day, :address, :average_rating, 
                :has_beach_nearby, :has_beds, :has_kitchen, 
                :has_swimming_pool, :has_hdtv, :has_bathtub, images: []
            )
    end

不确定这是否正确,但它似乎有效