Gem 有木有同学使用过 carrierwave + grape 构造文件上传 api 的

wuwx · January 23, 2014 · Last by ruby_sky replied at January 23, 2014 · 4191 hits

我弄了一个,结果怎么都传不上去文件,很痛苦啊

post do
  user = User.new
  user.photo = params[:photo]
  user.save
end
photo = {
    "file_data": base64.encodestring(open("default.jpg", 'rb').read()),
    "filename": "default.jpg",
}

然后用 python 送了个 photo 字段过去了……

你的做法,把二进制文件转换成 Base64 格式,然后附上文件名,Post 的方式发给 API,本身就是可行的,没有什么特别之处。

上传不上去的问题是什么?什么地方报错了?有没有 logs?

# -*- encoding: utf-8 -*-
require 'securerandom' unless defined?(::SecureRandom)
require 'mime/types' unless defined?(::MIME::Types)

module Helpers
  module File

    # 根据 RFC 2397 约定,将文件转字符
    def file_encode(file_path)
      return nil unless File.file?(file_path)

         file = File.open(file_path, 'rb')
         type = MIME::Types.type_for( File.basename(file_path) ).first.to_s
      content = Base64.encode64( file.read )

      return "data:#{type};base64,#{content}"
    end

    # 还原,文件信息
    def file_for(file_enc, attr_name)
      file_info = file_enc.match(/data:(.*);base64,([.\w\s\/\/+]*)/)
      if file_info && file_info.size == 3
        # 文件信息
        type      = file_info[1]
        content   = file_info[2]
        # 找扩展名
        mime_type = MIME::Types[file_info[1]].first
        extname   = mime_type.extensions.first
        # 文件名
        filename  = SecureRandom.hex(10) + ".#{extname}"

        return { 
          filename: filename, 
              type: type, 
              name: attr_name, 
          tempfile: file_restore(filename, content), 
              head: "Content-Disposition: form-data; name=\"#{attr_name}\"; filename=\"#{filename}\"\r\nContent-Type: #{type}\r\n"
        } 
      end
      return nil
    end

    # 还原,临时文件
    def file_restore(filename, content)
      temp_file = Tempfile.new(filename, binmode: true)
      temp_file.write( Base64.decode64(content) )
      temp_file.close

      return temp_file
    end
  end
end

#1 楼 @lgn21st 那个字段直接被过滤掉了,没有任何错误……

使用 python 的 requests 库上传文件,就解决了

Grape:

desc "update student profile", :params => {
  "profile[name]" => { desc: "student name" },
  "profile[avatar]" => { desc: "student avatar, please send a File", type: "file" }
}
put "profile" do
  current_user.student_profile.update_attributes!(params[:profile])
  present(current_user, with: ::Entities::User)
end

Spec:

it "should update student avatar profile" do
 avater_path = File.join(Rails.root, "spec/requests/api/GraduationIcon.png")

  params = {
    "profile[avatar]" => fixture_file_upload(avater_path, 'image/png')
  }
  put "/api/v1/students/profile.json", params, { "HTTP_X_USER_ACCESS_TOKEN" => @access_token }

  response.code.should == '200'
  response_body = JSON.parse(response.body)

  @student.reload
  @student.profile.avatar.file.filename.should == "GraduationIcon.png"
end

You need to Sign in before reply, if you don't have an account, please Sign up first.