通过Paperclip保存来自URL的图片

124
请告诉我一个使用Paperclip从URL保存图像的方法。
8个回答

202

在Paperclip 3.1.4中,它变得更加简单了。

def picture_from_url(url)
  self.picture = URI.parse(url)
end

这比使用open(url)稍微好一些。因为使用open(url)将会得到一个名为"stringio.txt"的文件名,而使用上述代码将根据URL获得一个正确的文件名。

self.picture = URI.parse("http://something.com/blah/avatar.png")

self.picture_file_name    # => "avatar.png"
self.picture_content_type # => "image/png"

3
从纸夹维基页面 https://github.com/thoughtbot/paperclip/wiki/Attachment-downloaded-from-a-URL:我在控制台上成功地运行了它,该应用程序在Heroku上。 - Donny Kurnia
3
对于S3的URL,我仍然得到application/octet_stream作为content_type - Joshua Pinter
7
如果你仍在使用已被弃用的Paperclip,请确保URI IO loader也被加载:可以在配置文件中添加Paperclip::UriAdapter.register,或者如果只需要临时解决问题,可以通过控制台暂时添加。 - Msencenb
方法是正确的,但在最新版本中语法已更改!请查看我下面的答案 => https://dev59.com/KG855IYBdhLWcg3w_5cQ#56039191 - Diego D
1
您需要在初始化器中添加条目 https://github.com/thoughtbot/paperclip/wiki/Attachment-downloaded-from-a-URL - ianpetzer

156

以下是一种简单的方式:

require "open-uri"

class User < ActiveRecord::Base
  has_attached_file :picture

  def picture_from_url(url)
    self.picture = open(url)
  end
end
然后只需:
user.picture_from_url "http://www.google.com/images/logos/ps_logo2.png"

7
如果需要使用 update_attributes,请将 picture_from_url 重命名为 picture_url=(value) - Daniel Rikowski
4
这可能存在安全隐患,因为用户可以调用user.picture_from_url('/etc/password')。在大多数情况下,这可能是可以接受的。 - David Tuite
1
为了安全起见,您应该将URI的方案列入白名单,并将私有主机列入黑名单。 - John Douthat
3
请见下方答案,有更好的解决方案。 - finiteautomata
6
使用 open(url) 方法时,文件名不准确,例如 open-uri20150106-10034-lpd5fm. 而非 ef3a601e_ef3d008b_ef3d0f7e.jpg - Joshua Pinter
@joshuaPinter 请使用URI.parse(url)代替open(url) - Simon Franzen

18

直到我使用“open”解析URI,它才对我起作用。一旦我添加了“open”,它就能正常工作了!

def picture_from_url(url)
  self.picture = URI.parse(url).open
end

我的纸夹版本是4.2.1

在打开之前,它无法正确检测内容类型,因为它不是一个文件。它会说image_content_type:"binary/octet-stream",即使我使用正确的内容类型覆盖它也不起作用。


谢谢!正是我自己所缺少的。 - Dan
如果你这样做,你将会失去你的扩展名。你需要在初始化程序中注册Paperclip::UriAdapter。然后你就不需要在结尾处使用.open了。请参阅https://github.com/thoughtbot/paperclip/wiki/Attachment-downloaded-from-a-URL。 - ianpetzer

15

首先使用 curb gem 下载镜像到一个 TempFile,然后只需分配此临时文件对象并保存您的模型即可。


2
我不明白这个答案有什么问题,我要投赞成票,因为我看到有一张反对票。 - jpemberthy
这是最高效的答案(远远超过)。我并不是一个真正的性能极客,但如果你在处理大文件,这确实会累积起来。 - Chip

8

这里报告了官方文档https://github.com/thoughtbot/paperclip/wiki/Attachment-downloaded-from-a-URL

无论如何,它似乎并没有更新,因为在最新版本的paperclip中有些事情已经发生了改变,因此这行代码不再有效:

user.picture = URI.parse(url)

它会引发一个错误,具体来说是此错误被引发:

Paperclip::AdapterRegistry::NoHandlerError: No handler found for #<URI:: ...

新的正确语法是这个:

url = "https://www.example.com/photo.jpeg"
user.picture = Paperclip.io_adapters.for(URI.parse(url).to_s, { hash_digest: Digest::MD5 })

同时,我们需要将以下代码添加到config/initializers/paperclip.rb文件中:
Paperclip::DataUriAdapter.register
Paperclip::HttpUrlProxyAdapter.register

我使用paperclip版本5.3.0测试过,它可以正常工作。


1
谢谢你,配置的东西解决了我的问题(Paperclip v6.1.0)。 - Owlvark
谢谢,迭戈。这也适用于PaperClip v6.0.0。 - Astm

3
这可能对你有所帮助。这里是使用 Paperclip 和远程 URL 中的图片的代码。
require 'rubygems'
require 'open-uri'
require 'paperclip'
model.update_attribute(:photo,open(website_vehicle.image_url))

在模型中
class Model < ActiveRecord::Base
  has_attached_file :photo, :styles => { :small => "150x150>", :thumb => "75x75>" }
end

3

由于这些是旧的回答,下面是一个更新的版本:

在数据库中为您所需的控制器添加图像远程URL

$ rails generate migration AddImageRemoteUrlToYour_Controller image_remote_url:string
$ rake db:migrate

编辑您的模型

attr_accessible :description, :image, :image_remote_url
.
.
.
def image_remote_url=(url_value)
  self.image = URI.parse(url_value) unless url_value.blank?
  super
end

*在Rails4中,您需要在控制器中添加attr_accessible。

如果允许其他人从URL上传图片,请更新您的表单

<%= f.input :image_remote_url, label: "Enter a URL" %>

super 是用来做什么的? - Terence Chow
函数 super 用于调用原始方法,查找方法体的起点是包含原始方法的对象的超类。 - Mini John

2

这是一种高级方法:

original_url = url.gsub(/\?.*$/, '')
filename = original_url.gsub(/^.*\//, '')
extension = File.extname(filename)

temp_images = Magick::Image.from_blob open(url).read
temp_images[0].write(url = "/tmp/#{Uuid.uuid}#{extension}")

self.file = File.open(url)

其中,Uuid.uuid只是生成一些随机的ID。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接