简体   繁体   English

将 CDN 与载波 + 雾在 s3 + Cloudfront 与 Rails 3.1 一起使用

[英]Use CDN with carrierwave + fog in s3 + cloudfront with rails 3.1

I'm using fog with carrierwave in my website.我在我的网站上使用带有carrierwave的 But the images load very very slowly.但是图像加载非常非常缓慢。

Then I want to speed up loading of images with a CDN.然后我想用 CDN 加速图像的加载。

I have followed this tutorial for create the CDN for images:我已经按照本教程为图像创建 CDN:

http://maketecheasier.com/configure-amazon-s3-as-a-content-delivery-network/2011/06/25 http://maketecheasier.com/configure-amazon-s3-as-a-content-delivery-network/2011/06/25

I have now my distribution deployed for images but I don't know how works fine the cdn.我现在已经为图像部署了我的发行版,但我不知道 cdn 是如何工作的。 I have in initializers/fog.rb the next configuration:我在 initializers/fog.rb 中有下一个配置:

CarrierWave.configure do |config|
  config.fog_credentials = {
    :provider               => 'AWS',
    :aws_access_key_id      => 'key',
    :aws_secret_access_key  => 'key',
    :region                 => 'eu-west-1'
  }
  config.fog_host = "http://da33ii2cvf53u.cloudfront.net" #config.asset_host instead of config.fog_host for new fog gem versions
  config.fog_directory  = 'pin-pro'
  config.fog_public     = false
  #config.fog_attributes = {'Cache-Control' => 'max-age=315576000'} 
end 

I dont know if this is correct, but in my local machine it does not works fine for me.我不知道这是否正确,但在我的本地机器上它对我不起作用。 I see the image location, is the same route as before:我看到图像位置,是和以前一样的路线:

https://s3-eu-west-1.amazonaws.com/pin-pro/uploads/pins/medium_610cafbe-5d43-4223-ab0e-daa4990863c4.jpg?AWSAccessKeyId=AKIAIDX34WHYKB3ZKFVA&Signature=RwQriNpiRXaTxyfYVvYjsvclUa8%3D&Expires=1333203059

How can I add a CDN to fog file in carrierwave with s3 and cloudfront?如何使用 s3 和 cloudfront 在carrierwave中将CDN添加到雾文件中?

It looks like you haven't added the line below to your config.看起来您还没有将下面的行添加到您的配置中。 You will need to replace the sample address below with your cloudfront address from Amazon.您需要将下面的示例地址替换为您在 Amazon 的 cloudfront 地址。

From the github README: https://github.com/jnicklas/carrierwave来自 github README: https : //github.com/jnicklas/carrierwave

"You can optionally include your CDN host name in the configuration. This is highly recommended, as without it every request requires a lookup of this information" “您可以选择在配置中包含您的 CDN 主机名。强烈建议这样做,因为如果没有它,每个请求都需要查找此信息”

config.asset_host = "http://c000000.cdn.rackspacecloud.com"

CarrierWave won't work when you set config.fog_public = false and point config.asset_host to a CloudFront distribution.当您设置config.fog_public = false并将config.asset_host指向 CloudFront 分配时,CarrierWave 将不起作用。 This has been documented multiple times:这已被多次记录:

https://github.com/carrierwaveuploader/carrierwave/issues/1158 https://github.com/carrierwaveuploader/carrierwave/issues/1215 https://github.com/carrierwaveuploader/carrierwave/issues/1158 https://github.com/carrierwaveuploader/carrierwave/issues/1215

In a recent project I was happy using CarrierWave to handle uploads to S3, but wanted it to return a signed CloudFront URL when using Model.attribute_url .在最近的一个项目中,我很高兴使用 CarrierWave 处理上传到 S3,但希望它在使用Model.attribute_url时返回一个签名的 CloudFront URL。 I came up with the following (admittedly ugly) workaround that I hope others can benefit from or improve upon:我想出了以下(无可否认的丑陋)解决方法,我希望其他人可以从中受益或改进:

Add the 'cloudfront-signer' gem to your project and configure it per the instructions.“cloudfront-signer” gem 添加到您的项目中并按照说明进行配置。 Then add the following override of /lib/carrierwave/uploader/url.rb in a new file in config/initializers (note the multiple insertions of AWS::CF::Signer.sign_url ):然后在config/initializers的新文件中添加/lib/carrierwave/uploader/url.rb的以下覆盖(注意AWS::CF::Signer.sign_url的多次插入):

module CarrierWave
      module Uploader
        module Url
          extend ActiveSupport::Concern
          include CarrierWave::Uploader::Configuration
          include CarrierWave::Utilities::Uri

          ##
          # === Parameters
          #
          # [Hash] optional, the query params (only AWS)
          #
          # === Returns
          #
          # [String] the location where this file is accessible via a url
          #
          def url(options = {})
            if file.respond_to?(:url) and not file.url.blank?
              file.method(:url).arity == 0 ? AWS::CF::Signer.sign_url(file.url) : AWS::CF::Signer.sign_url(file.url(options))
            elsif file.respond_to?(:path)
              path = encode_path(file.path.gsub(File.expand_path(root), ''))

              if host = asset_host
                if host.respond_to? :call
                  AWS::CF::Signer.sign_url("#{host.call(file)}#{path}")
                else
                  AWS::CF::Signer.sign_url("#{host}#{path}")
                end
              else
                AWS::CF::Signer.sign_url((base_path || "") + path)
              end
            end
          end

        end # Url
     end # Uploader
end # CarrierWave

Then override /lib/carrierwave/storage/fog.rb by adding the following to the bottom of the same file:然后通过将以下内容添加到同一文件的底部来覆盖/lib/carrierwave/storage/fog.rb

require "fog"

module CarrierWave
  module Storage
    class Fog < Abstract
       class File
          include CarrierWave::Utilities::Uri
          def url
             # Delete 'if statement' related to fog_public
             public_url
          end
       end
    end
  end
end

Lastly, in config/initializers/carrierwave.rb :最后,在config/initializers/carrierwave.rb 中

config.asset_host = " http://d12345678.cloudfront.net " config.asset_host = " http://d12345678.cloudfront.net "

config.fog_public = false config.fog_public = false

That's it.就是这样。 You can now use Model.attribute_url and it will return a signed CloudFront URL to a private file uploaded by CarrierWave to your S3 bucket.您现在可以使用 Model.attribute_url,它会将签名的 CloudFront URL 返回到 CarrierWave 上传到您的 S3 存储桶的私有文件。

似乎亚马逊 cdn 不适用于config.fog_public = false ,所以私有文件只能从 s3 访问,不能从 cdn

After some searching and struggling with this for a long time I found a page that says that CarrierWave doesn't support CloudFront signed urls.经过长时间的搜索和挣扎,我发现一个页面说 CarrierWave 不支持 CloudFront 签名的 url。 CloudFront signed urls are different than S3 signed urls, which caused me some confusion. CloudFront 签名的 url 与 S3 签名的 url 不同,这让我有些困惑。 Once I figured that out, it was a lot easier to know what to do.一旦我明白了这一点,就更容易知道该怎么做。

If you configure CarrierWave with config.fog_public = false then it will automatically begin signing S3 urls, but it can't be configured to work with Fog and CloudFront private content in the version of CarrierWave I'm using (1.0.0) .如果您使用config.fog_public = false配置 CarrierWave,那么它将自动开始对 S3 url 进行签名,但无法在我使用的 CarrierWave 版本(1.0.0)中将其配置为使用Fog和 CloudFront 私有内容。 I even tried using the carrierwave-aws gem and that didn't help either.我什至尝试使用carrierwave-aws gem,但也无济于事。

So what would happen is that CarrierWave would sign the URL and the host would look something like this:那么会发生的情况是 CarrierWave 会对 URL 进行签名,而主机看起来像这样:

https://my_bucket_name.s3-us-west-2.amazonaws.com/uploads/...?signature...

That points directly to the S3 bucket, but I needed it to point to CloudFront.这直接指向 S3 存储桶,但我需要它指向 CloudFront。 I needed the host to look like this:我需要主机看起来像这样:

https://s3.cloudfront_domain_name.com/uploads/...

And what would happen if I set config.asset_host equal to my CloudFront location is I'd get this, (with double slashes before "uploads"):如果我将config.asset_host设置为等于我的 CloudFront 位置会发生什么,我会得到这个(在“上传”之前使用双斜线):

https://s3.cloudfront_domain_name.com//uploads/...

That, too, made it clear CarrierWave wasn't yet designed to be used with CloudFront.这也清楚地表明 CarrierWave 尚未设计为与 CloudFront 一起使用。 Hopefully they'll improve it.希望他们会改进它。 This was my work-around.这是我的解决方法。 It's ugly, but it worked to get done what I needed without needing to modify CarrierWave itself, as I hope CarrierWave will at some point add support for CloudFront.这很丑陋,但它可以在不需要修改 CarrierWave 本身的情况下完成我需要的工作,因为我希望 CarrierWave 将在某个时候添加对 CloudFront 的支持。

  1. First I did a regex find/replace on my url and removed the S3 host portion and put on my CloudFront host portion.首先,我在我的 url 上做了一个正则表达式查找/替换并删除了 S3 主机部分并放在我的 CloudFront 主机部分上。 cf_url = s3_url.gsub("my_bucket_name.s3-us-west-2.amazonaws.com", "s3.cloudfront_domain_name.com")
  2. Next I did another regex find/replace to remove the S3 signed url at the end of the string: non_signed_cf_url = cf_url.gsub(/\\?.+/, '') This is because the signature will be incorrect because it was using the API for S3 and not for CloudFront for signing the URL.接下来我做了另一个正则表达式查找/替换以删除字符串末尾的 S3 签名 url: non_signed_cf_url = cf_url.gsub(/\\?.+/, '')这是因为签名将不正确,因为它使用的是用于 S3 而非 CloudFront 的 API 用于签署 URL。
  3. Now I re-sign the URL myself, using the cloudfront-signer gem: signed_cf_url = Aws::CF::Signer.sign_url(non_signed_cf_url, :expires => 1.day.from_now)现在我自己重新签名 URL,使用cloudfront-signer gem: signed_cf_url = Aws::CF::Signer.sign_url(non_signed_cf_url, :expires => 1.day.from_now)

There are a few other things you need to be aware of when serving private content on CloudFront:在 CloudFront 上提供私有内容时,您还需要注意一些其他事项:

  • In the Cache Behavior Settings for your path pattern (not necessarily the default one), set: "Restrict Viewer Access (Use Signed URLs or Signed Cookies)" to "Yes"在路径模式的缓存行为设置(不一定是默认设置)中,将:“限制查看者访问(使用签名 URL 或签名 Cookie)”设置为“是”
  • Set "Trusted Signers" to "self"将“可信签名者”设置为“自我”
  • Set "Query String Forwarding and Caching" to "Forward all, cache based on all" if you want to use other query strings more than the CloudFront signature in your url, such as response-content-disposition and response-content-type (I was able to get these to work successfully, but they have to be url_encoded properly.)如果您想在 url 中使用比 CloudFront 签名更多的其他查询字符串,例如response-content-dispositionresponse-content-type (I能够使这些成功工作,但它们必须正确进行 url_encoded。)
  • In your CloudFront Origin Settings, set your access-identity and set "Grant Read Permissions on Bucket" to "Yes, Update Bucket Policy"在您的 CloudFront Origin Settings 中,设置您的访问身份并将“Grant Read Permissions on Bucket”设置为“Yes, Update Bucket Policy”
  • In your General Distribution Settings, make sure "Distribution State" is "Enabled" and that you've added a CNAME to "Alternate Domain Names (CNAMEs)" if you're using one.在您的常规分发设置中,确保“分发状态”为“已启用”,并且如果您正在使用 CNAME,请确保已将 CNAME 添加到“备用域名 (CNAME)”。
  • If using a CNAME, make sure your DNS is correctly configured to point it to your CloudFront distribution's name.如果使用 CNAME,请确保您的 DNS 已正确配置以将其指向您的 CloudFront 分配的名称。
  • Lastly, once you set the configurations there is a long wait while AWS updates the distribution, so you won't see your changes happen right away.最后,一旦您设置了配置,AWS 更新分配需要等待很长时间,因此您不会立即看到您的更改发生。 It may seem like your app/website is still broken until the changes propagate through CloudFront.在更改通过 CloudFront 传播之前,您的应用程序/网站似乎仍然损坏。 This can make configuring it difficult because if you get it wrong you have to wait a long time before you can see your changes take effect and you may not be sure what happened.这会使配置变得困难,因为如果您弄错了,您必须等待很长时间才能看到更改生效,而且您可能不确定发生了什么。 But with these settings I was able to get it working for me.但是通过这些设置,我能够让它为我工作。
  • You can also create more than one caching path pattern so that some content is private and requires a CloudFront signed url, and other content isn't.您还可以创建多个缓存路径模式,以便某些内容是私有的并且需要 CloudFront 签名的 url,而其他内容则不需要。 For example, I set a path pattern of *.mp4 that requires a signature for all mp4 files and placed that above the default behavior.例如,我设置了一个*.mp4的路径模式,它需要所有 mp4 文件的签名,并将其置于默认行为之上。 And then I have the default cache behavior set to NOT require signed urls, which allows all other files - such as images - to be publicly accessible through the CloudFront distribution.然后我将默认缓存行为设置为不需要签名的 url,这允许所有其他文件 - 例如图像 - 可以通过 CloudFront 分发公开访问。

As mentioned before Fog or carrierwave-aws don´t allow to set private S3 Bucket and public access from a Cloudfront distribution.如前所述,Fog 或carrierwave-aws 不允许从 Cloudfront 发行版设置私有 S3 Bucket 和公共访问。 In order to do this you need to override the url method in your uploader like this:为此,您需要像这样覆盖上传器中的url方法:

def url(*args)
  if file.respond_to?(:url) and not file.url.blank?
    if args
      "https://*my_cloudfront_url*/#{store_dir}/#{args.join('_').to_s}_#{identifier}"
    else
      "https://*my_cloudfront_url*/#{store_dir}/#{identifier}"
    end
  elsif current_path
    File.expand_path(current_path).gsub(File.expand_path(public), '')
  end
end

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM