Как загрузить удаленное изображение с другого сайта в file_column в Ruby on Rails? - PullRequest
1 голос
/ 23 июля 2010

первый вопрос, надеюсь, я не напутал:)

Немного новичка по Ruby on Rails (также новичка по Ruby), который наткнулся на проблему с предполагаемым поведением приложения.

У меня есть file_column: изображение на изображении модели, принадлежащее модельному продукту, которое может иметь много изображений.

file_column прекрасно работает при использовании, так как я думаю, что он предназначен для использования и предназначен для загрузки изображений с использованием <% = file_column_field "picture", "image"%> и т. Д. Эта часть работает просто отлично.

Проблема возникает из-за наличия текстового поля, в котором пользователь может ввести css -selector для тега изображения на своем сайте (он зарегистрировал сайт и путь к странице, где должно быть изображение). Я не смог понять, как правильно загрузить изображение с этого другого сайта "под капотом".

Использование этих двух методов приводит к Не знаю, как обрабатывать строку со значением 'GIF89ad ..... , за которой следуют загрузки "двоичного".

Метод 1:

url = URI.parse(picture_www.external_url)
Net::HTTP.start(url.host, url.port) {|http|
  resp = http.get(url.path)
  picture_www.image = resp.body unless resp.nil?
}

Метод 2:

res = open(picture_www.external_url)
picture_www.image = res.read unless res.nil?

external_url содержит правильный URL-адрес, и загрузка идет нормально, так что, похоже, проблема в том, как я пытаюсь присвоить изображение полю file_column. Естественно, проблема может быть в том, как я загружаю образ, я понятия не имею, где, собственно, и заключается проблема ...:)

Кто-нибудь может мне помочь, пожалуйста?

Обновление:

Попытка использовать временный файл "вызывает неопределенный метод 'original_filename' для" и т. Д.

  Net::HTTP.start(url.host, url.port) {|http|
    resp = http.get(url.path)
    tempfile = Tempfile.new('test.jpg')
    File.open(tempfile.path, 'wb') do |f|
      f.write resp.body
    end
    picture_www.image = tempfile unless resp.nil?
  }

Update2:

Отладка показывает, что загруженный файл имеет атрибуты @content_type (например, «image / jpeg») и @original_path (имя файла без пути) в @_dc_obj и @tmpfile, когда созданный мной временный файл не имеет. Правильно ли их настроить, возможно, это сработает? Как мне правильно их настроить? И если правильно установить эти значения, будет ли загрузка файла выполнена "правильно"? После конечно же реструктуризации кода я получаю рабочее решение.

Update3:

Из ответа Минвера я получил решение проблемы "original_filename", и этот код, похоже, работает:

  io = open(picture_www.external_url)
  def io.original_filename; base_uri.path.split('/').last; end
  io.original_filename.blank? ? nil : io
  picture_www.image = io

Не знаю, правда, если это «правильный» способ сделать это или нет, но это то, что я буду использовать сейчас, если не появится какое-то «явно верное решение»:)

-Pkauko

Ответы [ 3 ]

1 голос
/ 05 февраля 2011

Метод UrlUpload Джо Мартинеса - хорошее решение, но в коде отсутствует ключевой метод.Если вы переопределяете метод method_missing , вы всегда должны также переопределять метод response_to? .В этом случае это особенно важно, так как некоторые программы используют response_to? при принятии решения о том, нужно ли делать multipart-post.

Например, камень Фарадея делает это:

def has_multipart?(body)
  body.values.each do |v|
    if v.respond_to?(:content_type)
      return true
    elsif v.respond_to?(:values)
      return true if has_multipart?(v)
    end
  end
  false
end

Итак, если вы собираетесь использовать приведенный выше код UrlUpload, я предлагаю вам добавить следующий метод:

  def respond_to?(symbol)
    attachment_data.respond_to?(symbol) || super
  end

Тогда Фарадей и другие связанные с ним драгоценные камни смогут использовать экземпляр этого класса длясоздать правильный multipart-post.

0 голосов
/ 26 июля 2010

Я не знаю, но, возможно, это то, что вы ищете.Когда вы сохраняете изображение, вы предоставляете css_selector и получаете взамен файл изображения.

Это представление:

<%= form_for(@image) do |f| %>

  <div class="field">
    <%= f.label :css_selector %><br />
    <%= f.text_field :css_selector %>
  </div>

  <div class="actions">
    <%= f.submit %>
  </div>

<% end %>

, а это модель:

class Picture < ActiveRecord::Base

  require 'open-uri' # Required to download the photo
  require 'mechanize' # Good gem to parse html pages

  belongs_to :product

  # Define the css_selector (not required as a filed in the database)
  attr_accessor :css_selector

  # Before we save the image, we download the photo if image has a css_selector value    
  before_save :download_remote_photo, :if => :css_selector_provided?

  private

    # Check if the attribute is provided      
    def css_selector_provided?
      !self.css_selector.blank?
    end

    # This method opens the page where the photo is
    # and grab the url to the image using a css-selector
    def fetch_photo_url
      agent = Mechanize::new
      page = agent.get(HERE_IS_THE_URL_TO_THE_PAGE_YOU_WANNA_SCRAPE)
      doc = Nokogiri::HTML(page.body)

      image_element = doc.at_css(self.css_selector) # Get the image on that page using the css selector
      image_url = image_element[:src]
    end

    def download_remote_photo
      self.image = do_download_remote_photo(fetch_photo_url)
    end

    def do_download_remote_photo(photo_url)
      io = open(URI.parse(URI.escape(photo_url)))
      def io.original_filename; base_uri.path.split('/').last; end
      io.original_filename.blank? ? nil : io
      rescue # catch url errors with validations instead of exceptions (Errno::ENOENT, OpenURI::HTTPError, etc...)
    end

end

Не проверял код, но я надеюсь, что вы поняли идею!

0 голосов
/ 23 июля 2010

Вот, пожалуйста,

require 'open-uri'

class UrlUpload
  EXTENSIONS = {
    "image/jpeg" => ["jpg", "jpeg", "jpe"],
    "image/gif" => ["gif"],
    "image/png" => ["png"]
  }
  attr_reader :original_filename, :attachment_data
  def initialize(url)
    @attachment_data = open(url)
    @original_filename = determine_filename
  end

  # Pass things like size, content_type, path on to the downloaded file
  def method_missing(symbol, *args)
    if self.attachment_data.respond_to? symbol
      self.attachment_data.send symbol, *args
    else
      super
    end
  end

  private
    def determine_filename
      # Grab the path - even though it could be a script and not an actual file
      path = self.attachment_data.base_uri.path
      # Get the filename from the path, make it lowercase to handle those
      # crazy Win32 servers with all-caps extensions
      filename = File.basename(path).downcase
      # If the file extension doesn't match the content type, add it to the end, changing any existing .'s to _
      filename = [filename.gsub(/\./, "_"), EXTENSIONS[self.content_type].first].join(".") unless EXTENSIONS[self.content_type].any? {|ext| filename.ends_with?("." + ext) }
      # Return the result
      filename
    end
end

# Make it always write to tempfiles, never StringIO
OpenURI::Buffer.module_eval {
  remove_const :StringMax
  const_set :StringMax, 0
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...