簡體   English   中英

如何從另一個站點將遠程映像下載到Ruby on Rails中的file_column?

[英]How do I download a remote image from another site to a file_column in Ruby on Rails?

第一個問題,希望我不會弄糟:)

Ruby on Rails的新手(也是Ruby新手),並且偶然發現了應用程序預期行為的問題。

我在屬於模型產品的模型圖片中有一個file_column:image,它可以包含許多圖片。

file_column在使用時工作得很好,因為我認為這是要使用的,並且使用<%= file_column_field“ picture”,“ image”%>等來上傳圖像。那部分工作得很好。

問題在於要有一個文本字段,以便用戶可以在其站點上為圖像標簽輸入css-選擇器(他們已經注冊了該站點以及應該放置圖像的頁面的路徑)。 我一直無法弄清楚如何從“其他”網站上正確下載該圖像。

使用這兩種方法都將導致“不知道如何處理帶有值'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字段的方式中。 自然地,問題可能出在我下載圖像的方式上,我不知道問題出在哪里,實際上是TBH ... :)

有人可以幫助我嗎?

更新:

嘗試使用臨時文件“導致未定義的方法'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?
  }

更新2:

調試顯示,當我創建的臨時文件沒有時,上載的文件在@_dc_obj和@tmpfile下具有@content_type(例如,“ image / jpeg”)屬性和@original_path(無路徑的文件名)屬性。 正確設置這些設置也許會使這項工作成功? 如何正確設置? 如果正確設置這些值,是否可以“正確”完成文件下載? 當我得到一個可行的解決方案后,當然要對代碼進行重組。

更新3:

從Minver的答案中,我得到了“ 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

雖然不知道這是否是執行此操作的“正確”方法,但這是我現在將要使用的方法,除非出現某些“顯然正確的執行方法”解決方案:)

-普考

Joe Martinez的UrlUpload方法是一個很好的解決方案,但是代碼缺少關鍵方法。 如果您超越了method_missing ,那么您也應該始終超越對response_to?的要求。 方法。 在這種情況下,這尤其重要,因為某些軟件使用了response_to ?? 在決定是否要進行多篇文章時。

例如,法拉第(Faraday)寶石執行以下操作:

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。

你去

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
}

我不知道,但是也許這就是您想要的。 保存圖像時,請提供一個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

尚未測試代碼,但希望您能理解!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM