我正在尝试为文件上传构建API后端。我希望能够使用具有Base64编码的文件字符串的POST请求上传文件。服务器应解码字符串,并使用CarrierWave保存文件。这是我到目前为止所做的:
photo.rb:
class Photo
include Mongoid::Document
include Mongoid::Timestamps
mount_uploader :image_file, ImageUploader
end
image_uploader.rb:
class ImageUploader < CarrierWave::Uploader::Base
storage :file
def store_dir
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
end
Rails控制台: (概要)
ruby-1.8.7-p334 :001 > img = File.open("../image.png") {|i| i.read}
=> "\377���JFIF\000\001\002\001\000H\000H\000\000\377�Photoshop 3.0\0008BIM\003...
ruby-1.8.7-p334 :003 > encoded_img = Base64.encode64 img
=> 3af8A\nmLpplt5U8q+a7G2...
ruby-1.8.7-p334 :005 > p = Photo.new
=> #<Photo _id: 4e21b9a31d41c817b9000001, created_at: nil, updated_at: nil, _type: nil, user_id: nil, image_file_filename: nil>
ruby-1.8.7-p334 :006 > p.user_id = 1
=> 1
ruby-1.8.7-p334 :007 > p.image_file = Base64.decode64 encoded_img
\255��=\254\200�7u\226���\230�-zh�wT\253%����\036ʉs\232Is�M\215��˿6\247\256\177...
ruby-1.8.7-p334 :008 > p.save
=> true
ruby-1.8.7-p334 :009 > p.image_file.url
=> nil
问题似乎与将Base64解码后的字符串转换为文件的过程有关。 CarrierWave似乎期待一个File对象,而我给它一个String。那么如何将该String转换为File对象。我希望这种转换不会将任何内容保存到文件系统中,只需创建对象并让CarrierWave完成剩下的工作。
答案 0 :(得分:24)
CarrierWave也接受一个StringIO,但它需要一个original_filename
方法,因为它需要它来计算文件名并进行扩展检查。如何在Rails 2和3之间进行更改,这里有两种方法:
Rails 2
io = StringIO.new(Base64.decode64(encoded_img))
io.original_filename = "foobar.png"
p.image_file = io
p.save
在Rails 3中,您需要创建一个新类,然后手动添加original_filename
返回
class FilelessIO < StringIO
attr_accessor :original_filename
end
io = FilelessIO.new(Base64.decode64(encoded_img))
io.original_filename = "foobar.png"
p.image_file = io
p.save
答案 1 :(得分:1)
您不必使用monkeypatch StringIO或将任何内容放入模型中。您可以覆盖上传器定义中的缓存!()方法。或者你可以更进一步,为自己制作一个模块。我的文件是来自json文档的序列化字符串。传入的对象看起来像这样{:filename =&gt; 'something.jpg',:filedata =&gt; base64字符串}。
这是我的模块:
module CarrierWave
module Uploader
module BackboneLink
def cache!(new_file=sanitized_file)
#if new_file isn't what we expect just jump to super
if new_file.kind_of? Hash and new_file.has_key? :filedata
#this is from a browser, so it has all that 'data:..' junk to cut off.
content_type, encoding, string = new_file[:filedata].split(/[:;,]/)[1..3]
sanitized = CarrierWave::SanitizedFile.new(
:tempfile => StringIO.new(Base64.decode64(string)),
:filename => new_file[:filename],
:content_type => content_type
)
super sanitized
else
super
end
end
end
end
end
然后我可以将它包含在上传器中。 上传/ some_uploader.rb:
class SomeUploader < CarrierWave::Uploader::Base
include CarrierWave::Uploader::BackboneLink
答案 2 :(得分:0)
class AppSpecificStringIO < StringIO
attr_accessor :filepath
def initialize(*args)
super(*args[1..-1])
@filepath = args[0]
end
def original_filename
File.basename(filepath)
end
end
另见carrierwave wiki https://github.com/carrierwaveuploader/carrierwave/wiki/How-to%3A-Upload-from-a-string-in-Rails-3