I am building a rails app with carrierwave and fog for attachment storage. In my test environment, I am using fog local storage.
I am looking for a way to get the full attachment path with this configuration.
CarrierWave.configure do |config|
config.fog_credentials = {
provider: 'Local',
local_root: '/Users/me/fog',
endpoint: '/Users/me/fog',
}
config.fog_directory = 'test.myapp.com
config.fog_public = false
config.fog_attributes = { 'Cache-Control' => 'max-age=315576000' }
end
When I use any other storage options (like AWS S3), I can get the full url to an attachment just by doing my_object.my_attachment_url or my_object.my_attachment.path.
However, when using Local storage, I only get a relative path to my configuration options like my_object/my_attachment/1/test.jpg.
Is there any way through carrierwave or fog to get the full path to this local file?
For my example, the output I am looking for would be: /Users/me/fog/test.myapp.com/my_object/my_attachment/1/test.jpg
For me, the answer was modifying to carrierwave uploader class.
I had
def store_dir
"#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
which worked fine for AWS S3 as all the S3 specific info was inserted before this string. However, to get this to work with fog Local as well, I added:
if Rails.env.test?
def base_path
"#{File.expand_path(CONFIG.fog_local_root)}/#{CONFIG.fog_directory}/"
end
else
def base_path
''
end
end
Related
Carrierwave is returning a JSON response like this:
"url": "/mys3bucket/uploads/entrees/photo/32/4c312e9aed37a59319096a03_1.jpg",
I need the absolute url. Images are hosted on Amazon S3. How can I get the absolute url?
My temporary hack is to add following to Carrierwave initializer:
config.asset_host = "s3.#{ENV.fetch('AWS_REGION')}.amazonaws.com/mybucket"
CarrierWave uses the combination of the filename and the settings
specified in your uploader class to generate the proper URL. This
allows you to easily swap out the storage backend without making any
changes to your core application.
That said, you cannot store the full URL. You can set CarrierWave's asset_host config setting that is based on envrionment.
What storage are you using on Production? Here is my configuration and It works very well. Hope it helps.
CarrierWave.configure do |config|
config.root = Rails.root
if Rails.env.production?
config.storage = :fog
config.fog_credentials = {
provider: "AWS",
aws_access_key_id: ENV["AWS_ACCESS_KEY_ID"],
aws_secret_access_key: ENV["AWS_SECRET_ACCESS_KEY"],
region: ENV["S3_RESION"]
}
config.fog_directory = ENV["S3_BUCKET_NAME"]
# config.asset_host = ENV["S3_ASSET_HOST"]
else
config.storage = :file
# config.asset_host = ActionController::Base.asset_host
end
end
So this seems like it should be quite easy... everyone is saying just to use config.asset_host. When I set that though, all the links inside my app still point to S3.
CarrierWave.configure do |config|
config.storage = :fog
config.fog_credentials = {
:provider => 'AWS',
:aws_access_key_id => AWS_ACCESS_KEY_ID,
:aws_secret_access_key => AWS_SECRET_ACCESS_KEY,
:region => 'us-east-1'
}
config.fog_authenticated_url_expiration = 3.hours
config.asset_host = "http://xyz123.cloudfront.net"
config.fog_directory = S3_BUCKET_NAME
config.fog_public = false
config.fog_attributes = {
'Cache-Control' => "max-age=#{1.year.to_i}"
}
end
here is how I call my files...
image_tag book.attachments.first.filename.file.authenticated_url(:thumb175)
It looks to me like public_url prepends the proper host, but it takes 0 arguments... so how am I supposed to pass the proper response-content-disposition and response-content-type and the link expire time?
I had the same problem and spent far too long figuring out the answer! It turns out that when you set fog_public = false CarrierWave will ignore config.asset_host. You can demo this by setting config.fog_public = true: your URLs will now be CloudFront URLs rather than S3 URLs. This issue has been raised previously:
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. 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. 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):
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:
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.asset_host = "http://d12345678.cloudfront.net"
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.
I think you found this for yourself, but public urls will not expire. If you want that you'll need to use authenticated urls. For public urls I think you can simply get the url and append whatever query params you would like, at least for now. If that works well for you, we can certainly see about patching things to do the right thing.
I am using carrierwave with fog to create a simple image uploading process on my server.
The goal is to have images stored in my server in this folder :
/opt/myapp/uploads/
I have configured carrierwave and fog with that parameters and the upload is working very well :
CarrierWave.configure do |config|
config.fog_credentials = {
:provider => 'Local',
:local_root => '/opt/myapp/'
}
config.fog_public = false
config.fog_directory = 'uploads/'
config.storage = :fog
config.asset_host = proc do |file|
'/opt/myapp/uploads/'
end
end
When I upload an image I can see that it is stored in the corresponding folder. But how can I display them in my web pages ?
The generated url is
http://localhost:3000/opt/myapp/uploads/<path-to-my-image>.png
So my application tries to get images from an opt/ folder in my rails application directory, but how could I tell it to retrieve them from the server filesystem instead ?
Ok,that was easy to do :
First add a route for the corresponding urls :
match '/opt/myapp/uploads/:file_name' => 'files#serve'
The create a FilesController with a serve method :
class FilesController < ApplicationController
def serve
before_filter :authenticate_user! #used with Devise to protect the access to the images
path = "/opt/myapp/uploads/#{params[:file_name]}.png"
send_file( path,
:disposition => 'inline',
:type => 'image/png',
:x_sendfile => true )
end
end
Then I needed to add this line in my development.rb and production.rb configuration files :
config.action_dispatch.x_sendfile_header = "X-Accel-Redirect" #to use with Thin and Nginx
I'd like to download an image that was uploaded to S3 using carrierwave. The image is on the Card model, mounted as an uploader. I saw this answer, but had trouble getting that solution to work. My code is:
#download image from S3
uploader = card.image #image is the mounted uploader
uploader.retrieve_from_store!(File.basename(card.image.url))
uploader.cache_stored_file!
that last line throws: "... caused an exception (undefined method `body' for nil:NilClass)..."
My carrierwave config looks like:
#config/initializers/carrierwave.rb
CarrierWave.configure do |config|
config.storage = :fog
config.cache_dir = "#{Rails.root}/tmp/upload"
...
end
Thanks apneadiving. It was as easy as:
image = MiniMagick::Image::open(card.image.to_s)
image.write(somepath)
I have tried this in Rails 5 to download file from AWS S3.
def download
image = card.image
# validate existing image from AWS S3
if image.try(:file).exists?
data = open(image.url)
send_data data.read, type: data.content_type, x_sendfile: true
end
end
I hope help everyone.
I'm having this problem with carrierwave+fog+s3 with Amazon cloud front. With the following setup I can upload files to s3 but after uploaded, the S3 Objects URLs I get from my rails app doesn't have the assets_host based URLs i.e I'm expeting the URLs to be looks like this format https://mycloudfrontname.cloudfront.net/uploads/myfile.mp3
But they all appear in this format https://mybucketname.s3.amazonaws.com/uploads/myfile.mp3
What might be wrong here?
CarrierWave.configure do |config|
config.fog_credentials = {
:provider => 'AWS',
:aws_access_key_id => 'XXXX',
:aws_secret_access_key => 'XXXX',
:region => 'us-east-1'
}
config.fog_directory = 'mybucketname'
config.asset_host = 'https://mycloudfrontname.cloudfront.net'
config.fog_public = false
config.fog_attributes = {'Cache-Control' => 'max-age=315576000'}
end
UPDATE:
I found this code bit from Carrierwave's /lib/carrierwave/storage/fog.rb - So if we set the asset_host as on above code snippet this must work right? or is there any other configuration I must do as well?
def public_url
if host = #uploader.asset_host
if host.respond_to? :call
"#{host.call(self)}/#{path}"
else
"#{host}/#{path}"
end
else
# AWS/Google optimized for speed over correctness
case #uploader.fog_credentials[:provider]
when 'AWS'
# if directory is a valid subdomain, use that style for access
if #uploader.fog_directory.to_s =~ /^(?:[a-z]|\d(?!\d{0,2}(?:\d{1,3}){3}$))(?:[a-z0-9\.]|(?![\-])|\-(?![\.])){1,61}[a-z0-9]$/
"https://#{#uploader.fog_directory}.s3.amazonaws.com/#{path}"
else
# directory is not a valid subdomain, so use path style for access
"https://s3.amazonaws.com/#{#uploader.fog_directory}/#{path}"
end
when 'Google'
"https://commondatastorage.googleapis.com/#{#uploader.fog_directory}/#{path}"
else
# avoid a get by just using local reference
directory.files.new(:key => path).public_url
end
end
end
Change config.fog_public to true and add config.asset_host = 'YOUR_CND_ADDRESS'. The asset_host doesn't work when fog_public is false
In your environment file you need to set the asset host. Just add the line below to your config/environments/production.rb file, and you should be okay. Also may want to make sure you're using the latest version of carrierwave and fog gems.
-- config/environments/production.rb
Myapp::Application.configure do
# Use Content Delivery Network for assets
config.action_controller.asset_host = 'https://mycloudfrontname.cloudfront.net'
end
Don't use asset_host. The asset_host setting is for files served by the rails asset helpers. CarrierWave files are handled in a different manner. The config you are looking for is config.fog_host
config.fog_host = 'https://mycloudfrontname.cloudfront.net'