Rails 3中图片路径的完整URL

38

我有一张图片,其中包含了使用CarrierWave上传的内容:

Image.find(:first).image.url #=> "/uploads/image/4d90/display_foo.jpg"

在我的看法中,我想要找到这个的绝对URL。将root_url附加上去会导致一个双斜杠/

root_url + image.url #=> http://localhost:3000//uploads/image/4d90/display_foo.jpg

我不能使用我所知道的url_for,因为它只允许传递一条路径或一个选项列表来识别资源和:only_path选项。由于没有可以通过“控制器”+“操作”来识别的资源,所以我不能使用:only_path选项。

url_for(image.url, :only_path => true) #=> wrong amount of parameters, 2 for 1

在Rails3中,创建完整URL的最干净和最好的方法是什么?

9个回答

100
您可以像这样设置CarrierWave的asset_host配置设置:

# config/initializers/carrierwave.rb
CarrierWave.configure do |config|
  config.storage = :file
  config.asset_host = ActionController::Base.asset_host
end

这个符号 ^ 告诉CarrierWave使用你的应用程序中的 config.action_controller.asset_host 设置,该设置可以在其中一个 config/envrionments/[environment].rb 文件中定义。请参见此处获取更多信息。

或者显式地设置它:

  config.asset_host = 'http://example.com'

重新启动您的应用程序,就可以继续使用 - 不需要任何辅助方法。
* 我正在使用Rails 3.2和CarrierWave 0.7.1

我喜欢这个,简洁明了。 - Joe Czucha

33

尝试使用path方法

Image.find(:first).image.path

更新

request.host + Image.find(:first).image.url

并且你可以将它包装为一个辅助函数,使其永久地保持DRY。

request.protocol + request.host_with_port + Image.find(:first).image.url

返回整个(系统)路径。 => "/home/..../public/uploads/image/4d90/foo.jpg" 我需要URL而不是路径,以便在Atom源中使用。 - berkes
哦,是的。然后尝试 request.host + Image.find(:first).image.url - fl00r
1
也不是 :( "localhost/uploads/image/4d90/display_foo.jpg" 在开发环境中缺少 :3000 端口和 http(s)://。 - berkes
2
request.protocol + request.host_with_port + Image.find(:first).image.url - fl00r
2
我正在使用延迟作业来排队发送电子邮件的工作。这些工作会渲染电子邮件模板。然而,此时请求将为nil(它不再是HTTP请求的一部分)。 - lulalala

11

另一个简单的方法是使用URI.parse,对于您的情况应该是:

require 'uri'

(URI.parse(root_url) + image.url).to_s

还有一些例子:

1.9.2p320 :001 > require 'uri'
 => true 
1.9.2p320 :002 > a = "http://asdf.com/hello"
 => "http://asdf.com/hello" 
1.9.2p320 :003 > b = "/world/hello"
 => "/world/hello" 
1.9.2p320 :004 > c = "world"
 => "world" 
1.9.2p320 :005 > d = "http://asdf.com/ccc/bbb"
 => "http://asdf.com/ccc/bbb" 
1.9.2p320 :006 > e = "http://newurl.com"
 => "http://newurl.com" 
1.9.2p320 :007 > (URI.parse(a)+b).to_s
 => "http://asdf.com/world/hello" 
1.9.2p320 :008 > (URI.parse(a)+c).to_s
 => "http://asdf.com/world" 
1.9.2p320 :009 > (URI.parse(a)+d).to_s
 => "http://asdf.com/ccc/bbb" 
1.9.2p320 :010 > (URI.parse(a)+e).to_s
 => "http://newurl.com" 

5

仅采用floor的答案并提供助手:

# Use with the same arguments as image_tag. Returns the same, except including
# a full path in the src URL. Useful for templates that will be rendered into
# emails etc.
def absolute_image_tag(*args)
  raw(image_tag(*args).sub /src="(.*?)"/, "src=\"#{request.protocol}#{request.host_with_port}" + '\1"')
end

2
这个帮助类在标准视图中使用效果很好,但是你不能在邮件视图中使用它,因为邮件发送器没有"请求"的概念,所以你的帮助类会失败。 - Eric
我不确定,但我认为在Rails 3.1和资产管道中,您可以使用资产主机来组合完整的URL。在早期版本中,我认为需要硬编码主机。 - Martin T.
@M.G.Palmer,这仍然有效吗?因为CarrierWave上传的图像驻留在公共文件夹内。 - lulalala
@lulalala 我认为是这样的,也许你需要使用类似 absolute_image_tag("/../#{filename}") 的方式来调用,以从默认的 /images 路径中“上升”。但我从未使用过 carrierwave,所以可能会有所不同。 - Martin T.

2

这里有很多答案。然而,我不喜欢它们中的任何一个,因为它们都依赖于我记住显式添加端口、协议等信息。我认为以下是最优雅的方法:

full_url = URI( root_url )
full_url.path = Image.first.image.url
# Or maybe you want a link to some asset, like I did:
# full_url.path = image_path("whatevar.jpg")
full_url.to_s

最好的事情在于我们可以轻松地只更改一个东西,无论是什么东西,你总是以相同的方式进行操作。比如说,如果你想要删除协议并使用协议相对URL,那么请在最终转换为字符串之前进行此操作。

full_url.scheme = nil

太好了,现在我有一种方法可以将我的资产图像url转换为协议相对url,我可以在代码片段中使用它,其他人可能希望将其添加到他们的网站上,无论他们在网站上使用哪种协议,它都可以正常工作(前提是你的网站支持任一协议)。


2

我使用了default_url_options,因为在邮件发送器中没有request可用,并且避免了在config.action_controller.asset_host中重复指定主机名(如果之前没有指定)。

config.asset_host = ActionDispatch::Http::URL.url_for(ActionMailer::Base.default_url_options)

1

我发现了一种避免双斜杠的技巧:

URI.join(root_url, image.url)

1

您无法在电子邮件中引用请求对象,那么怎么办呢:

def image_url(*args)
    raw(image_tag(*args).sub /src="(.*?)"/, "src=\"//#{ActionMailer::Base.default_url_options[:protocol]}#{ActionMailer::Base.default_url_options[:host]}" + '\1"')
end

1
你实际上可以很容易地通过以下方式完成此操作:

root_url[0..-2] + image.url

我同意它看起来不太好,但是能完成工作.. :)


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