其实想说的是如何通过JSON文件的方式来上传图片。
问题描述
如标题所述,需要通过接口将图片上传到ROR项目中,如何实现?
解答
先说一个简单的。
以一个model picture为例,看一下具体步骤:
model picture的信息:
Table name pictures
id          :integer
file_name    :string
image       :string
这里,设置routes, 建model,建controller的过程省略,可参见:Rails:Web API接口实作。
分三步走:
- 
编辑 Gemfile gem 'carrierwave' gem 'mini_magick'终端执行 bundle,重启服务器。
- 新增uploader - 执行: - rails g uploader PicImage- 编辑 - app/uploaders/pic_image_uploader.rb- class PicImageUploader < CarrierWave::Uploader::Base + include CarrierWave::MiniMagick ...... end- 编辑 - app/models/picture.rb,把 carrierwave 的 Uploader 挂上去。- class Picture < ApplicationRecord + mount_uploader :image, PicImageUploader ...... end
- 设定create 
  编辑app/controllers/api/v1/pictures_controller.rb 中,create部分:
# upload image through api
class Api::V1::PicturesController < ApiController
  before_action :authenticate_user!, only: %i[create]
  def create
    @picture = Picture.new(picture_params)  
    if @picture.save
      render json: { file_name: @picture.file_name, image: @picture.image }
    else
      render json: { message: 'failed', error: @picture.errors }, status: 400
    end
  end
  .......
  private
  def picture_params
    params.permit(:file_name, :image)
  end
end
再来个复杂的。👇
如何通过JSON文件来上传?
在搜索解答的过程中,发现了另一种方式,假定你需要通过JSON文件的方式去上传图片,如何实现?
即接口调用是这样的:/api/v1/pictures.json,由于JSON不支持嵌入式文件,所以会有些波折。
具体的实现方式是先将需要上传的图片file经base64编译后,调用接口创建一个实例, 然后在picture controller的create中,对传递过来的参数file_base64进行解析, 将解析后的文件写入temfile,并通过ActionDispatch::Http::UploadedFile来新建一个uploader file,如此,一个image从encoding到decoding的过程结束,再使用Picture.new创建即可。
具体来说,前面的步骤都一样,只是create部分会有些不同。
先给picture新增一个字段,file_base64, 它是图片经base64编译后得到的字符串。
执行:
rails g migration add_file_base64_to_pictures file_base64:string
rake db:migrate
picture.json文件长这样:
picture {:file_base64 => "file_base64", :file_name => "file_name"}
  编辑app/controllers/api/v1/pictures_controller.rb 中,create部分:
class Api::V1::PicturesController < ApiController
  before_action :authenticate_user!, only: %i[create]
  def create
    if params[:picture][:file_base64]
      # create a new tempfile named file
      tempfile = Tempfile.new('file')
      tempfile.binmode
      # get the file ,decode it with base64, write it to the tempfile
      tempfile.write(Base64.decode64(params[:picture][:file_base64]))
      # create a new uploaded file
      @uploaded_file = ActionDispatch::Http::UploadedFile.new(tempfile: tempfile, filename: params[:picture][:file_name])
      @picture = Picture.new(params[:picture])
      @picture.image = @uploaded_file
      # delete the tempfile
      tempfile.delete
    end
    if @picture.save
      render json: @picture
    else
      render json: { message: 'failed', error: @picture.errors }, status: 400
    end
  end
end
这里,create部分显得很臃肿,我们skinny一下,改成这样:
class Api::V1::PicturesController < ApiController
before_action :authenticate_user!, only: %i[create]
before_action :process_file, only: %i[create]
def create
  if @picture.save
    render json: @picture
  else
    render json: { message: 'failed', error: @picture.errors }, status: 400
  end
end
private
def process_file
  if params[:picture][:file_base64]
    tempfile = Tempfile.new('file')
    tempfile.binmode
    tempfile.write(Base64.decode64(params[:picture][:file_base64]))
    @uploaded_file = ActionDispatch::Http::UploadedFile.new(tempfile: tempfile, filename: params[:picture][:file_name])
    @picture = Picture.new(params[:picture])
    @picture.image = @uploaded_file
    tempfile.delete
  end
end
end
呃,貌似process_file还是有点臃肿, 可以继续拆,这里就不实作了。
OK. 用postman测试一下,确认正常。
这里,你可能会问,编译后的文件在哪呢?在public/uploaders/picture/image下。
此外,上传的文件不需要 git commit, 在 .gitignore 中添加 public/uploads 这个目录即可。
参考
Sending files to a Rails JSON API