Ruby-on-rails Rails 3 获取原始发布数据并将其写入 tmp 文件

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4704143/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-03 00:07:19  来源:igfitidea点击:

Rails 3 get raw post data and write it to tmp file

ruby-on-railsruby-on-rails-3http-post

提问by Andrew

I'm working on implementing Ajax-Uploadfor uploading photos in my Rails 3 app. The documentation says:

我正在实施Ajax-Upload,以便在我的 Rails 3 应用程序中上传照片。文档说:

  1. For IE6-8, Opera, older versions of other browsers you get the file as you normally do with regular form-base uploads.

  2. For browsers which upload file with progress bar, you will need to get the raw post data and write it to the file.

  1. 对于 IE6-8、Opera、其他浏览器的旧版本,您可以像通常使用常规的基于表单的上传一样获取文件。

  2. 对于使用进度条上传文件的浏览器,您需要获取原始帖子数据并将其写入文件。

So, how can I receive the raw post data in my controller and write it to a tmp file so my controller can then process it? (In my case the controller is doing some image manipulation and saving to S3.)

那么,如何在我的控制器中接收原始发布数据并将其写入 tmp 文件,以便我的控制器可以处理它?(在我的例子中,控制器正在做一些图像处理并保存到 S3。)

Some additional info:

一些额外的信息:

As I'm configured right now the post is passing these parameters:

正如我现在配置的那样,帖子正在传递这些参数:

Parameters:
{"authenticity_token"=>"...", "qqfile"=>"IMG_0064.jpg"}

... and the CREATE action looks like this:

... 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

... but I get this error:

...但我收到此错误:

ActiveRecord::RecordInvalid (Validation failed: File file name must be set.):
  app/controllers/attachments_controller.rb:7:in `create'

回答by Stefaan Colman

That's because params[:qqfile] isn't a UploadedFile object but a String containing the file name. The content of the file is stored in the body of the request (accessible by using request.body.read). Ofcourse, you can't forget backward compatibility so you still have to support UploadedFile.

那是因为 params[:qqfile] 不是 UploadedFile 对象,而是一个包含文件名的字符串。文件的内容存储在请求的正文中(可使用request.body.read访问)。当然,你不能忘记向后兼容,所以你仍然必须支持UploadedFile。

So before you can process the file in a uniform way you have to catch both cases:

因此,在以统一方式处理文件之前,您必须捕获两种情况:

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

回答by super_p

try it, add lib/qq_file.rb:

试试吧,添加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

in assets_controller type this:

在 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:

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}
});

回答by tomaszbak

Another solution is:

另一种解决方案是:

gem 'rack-raw-upload', :git => 'git://github.com/tb/rack-raw-upload.git'

and in config.ru:

在 config.ru 中:

 require 'rack/raw_upload'
 use Rack::RawUpload

and use params[:file] in controller.

并在控制器中使用 params[:file]。

回答by Peter Ehrlich

Rather than creating some clutter temp file, you can use StringIO. See my answer about CarrierWave, here: https://stackoverflow.com/a/8812976/478354

您可以使用 StringIO,而不是创建一些杂乱的临时文件。在此处查看我关于 CarrierWave 的回答:https://stackoverflow.com/a/8812976/478354