Rails 3 получает исходные данные и записывает их в файл tmp
Я работаю над реализацией Ajax-Upload для загрузки фотографий в моем приложении Rails 3. В документации указано:
-
Для IE6-8, Opera, более старых версий других браузеров вы получаете файл как вы обычно делают с обычной формой загрузки.
-
Для браузеров, которые загружают файл с индикатором выполнения, вам необходимо получить исходных данных и записать их в файл.
Итак, как я могу получить исходные данные в моем контроллере и записать его в файл tmp, чтобы мой контроллер мог его обработать? (В моем случае контроллер выполняет некоторые манипуляции с изображениями и сохраняет их на S3.)
Дополнительная информация:
Поскольку я настроен прямо сейчас, сообщение передает эти параметры:
Parameters:
{"authenticity_token"=>"...", "qqfile"=>"IMG_0064.jpg"}
... и действие CREATE выглядит так:
def create
@attachment = Attachment.new
@attachment.user = current_user
@attachment.file = params[:qqfile]
if @attachment.save!
respond_to do |format|
format.js { render :text => '{"success":true}' }
end
end
end
... но я получаю эту ошибку:
ActiveRecord::RecordInvalid (Validation failed: File file name must be set.):
app/controllers/attachments_controller.rb:7:in `create'
Ответы
Ответ 1
Это потому, что params [: qqfile] не является объектом UploadedFile, а строкой, содержащей имя файла. Содержимое файла сохраняется в теле запроса (доступно с помощью request.body.read). Конечно, вы не можете забыть о обратной совместимости, поэтому вам все равно придется поддерживать UploadedFile.
Итак, прежде чем вы сможете обрабатывать файл в унифицированном виде, вам нужно поймать оба случая:
def create
ajax_upload = params[:qqfile].is_a?(String)
filename = ajax_upload ? params[:qqfile] : params[:qqfile].original_filename
extension = filename.split('.').last
# Creating a temp file
tmp_file = "#{Rails.root}/tmp/uploaded.#{extension}"
id = 0
while File.exists?(tmp_file) do
tmp_file = "#{Rails.root}/tmp/uploaded-#{id}.#{extension}"
id += 1
end
# Save to temp file
File.open(tmp_file, 'wb') do |f|
if ajax_upload
f.write request.body.read
else
f.write params[:qqfile].read
end
end
# Now you can do your own stuff
end
Ответ 2
попробуйте, добавьте lib/qq_file.rb:
# encoding: utf-8
require 'digest/sha1'
require 'mime/types'
# Usage (paperclip example)
# @asset.data = QqFile.new(params[:qqfile], request)
class QqFile < ::Tempfile
def initialize(filename, request, tmpdir = Dir::tmpdir)
@original_filename = filename
@request = request
super Digest::SHA1.hexdigest(filename), tmpdir
fetch
end
def self.parse(*args)
return args.first unless args.first.is_a?(String)
new(*args)
end
def fetch
self.write @request.raw_post
self.rewind
self
end
def original_filename
@original_filename
end
def content_type
types = MIME::Types.type_for(@request.content_type)
types.empty? ? @request.content_type : types.first.to_s
end
end
в файле assets_controller:
def create
@asset ||= Asset.new(params[:asset])
@asset.assetable_type = params[:assetable_type]
@asset.assetable_id = params[:assetable_id] || 0
@asset.guid = params[:guid]
@asset.data = QqFile.parse(params[:qqfile], request)
@asset.user_id = 0
@success = @asset.save
respond_with(@asset) do |format|
format.html { render :text => "{'success':#{@success}}" }
format.xml { render :xml => @asset.to_xml }
format.js { render :text => "{'success':#{@success}}"}
format.json { render :json => {:success => @success} }
end
end
JavaScript:
var photo_uploader = new qq.FileUploader({
element: document.getElementById('photo-button'),
multiple: true,
action: '/assets',
allowedExtensions: ['png', 'gif', 'jpg', 'jpeg'],
sizeLimit: 2097152,
params: {guid: $('#idea_guid').val(), assetable_type: 'Idea', klass: 'Picture', collection: true}
});
Ответ 3
Другое решение:
gem 'rack-raw-upload', :git => 'git://github.com/tb/rack-raw-upload.git'
и в config.ru:
require 'rack/raw_upload'
use Rack::RawUpload
и использовать параметры [: файл] в контроллере.
Ответ 4
Вместо создания некоторого временного файла, вы можете использовать StringIO. См. Мой ответ о CarrierWave, здесь: fooobar.com/info/395256/...