Image is 404 even though on the rails server - ruby-on-rails

I've used the very good tutorial on railscast about carrierwave to upload pictures on the server, but even thought it is working perfectly in dev, in production it's not working anymore.
In my uploader I have this:
def store_dir
"images/profile/#{model.id}"
end
the image is properly uploaded and is there but I get a 404 when I call the image...
Any ideas are wellcomed.

If your using a hosting service such as Heroku you won't be able to just upload a file through carrierwave in production directly to your app and expect it to stay there (it may appear momentarily but not for long). You need to use an image hosting service such as aws, S3, etc. to integrate and "store" your files.
Also, the store_dir should probably look closer to something like this
def store_dir
"uploads/#{Rails.env}/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
Not sure this is your issue but hope it helps!

Related

Refile upload to S3 still returns localhost upload

I followed the tutorial at refile/refile and try to use `refile-s3 to upload my photos to S3. However, it is still uploading to my localhost.. I wonder why??
Code in Github: https://github.com/chrisyeung1121/refile-example
Am I missing anything?
I ran the example app and everything is done correctly, the file is uploaded to S3. You can see this when viewing your s3 bucket there should be a /store folder with a file inside thats looks similar to this: 8d005532259e4abc0..... It will correspond with the id saved in your database. In your case the id is stored as profile_image_id in your User model.
It may be confusing that the result of attachment_url is returning <img src="/attachments/store/fill/300/300/8d005532259e4abc0...../profile_image.jpg" alt="Profile image"> making it seem like it is still being uploaded to the localhost.
What is happening is that refile downloads the file from amazon s3, modifies it to 300x300 on the fly and sends it to the browser. However this shouldn't be done every time the page is loaded since it would waste a lot of processing power. This is where a CDN is needed, and refile is designed to use one. This way the processed imaged gets cached and everything is much faster.
in the refile.rb config file:
Refile.cdn_host = "https://your-dist-url.cloudfront.net"
or in my case I restricted it to production:
if Rails.env.production?
Refile.cdn_host = "https://your-dist-url.cloudfront.net"
end
note that .cdn_host should be .host if using a version below 0.6.0
Now this should return something like <img src="https://your-dist-url.cloudfrond.net/attachments/store/fill/300/300/8d005532259e4abc0...../profile_image.jpg" alt="Profile image">
You can read more about it in the official documentation.

How can I migrate CarrierWave files to a new storage mechanism?

I have a Ruby on Rails site with models using CarrierWave for file handling, currently using local storage. I want to start using cloud storage and I need to migrate existing local files to the cloud. I am wondering if anyone can point out a method for doing this?
Bonus points for using a model attribute that would allow me to do this row-by-row in the background without interrupting my site for extended downtime (in other words, some model rows would still have local storage while others used cloud storage).
My first instinct is to create a new uploader for each model that uses cloud storage, so I have two uploaders on each model, then transferring the files from one to the other, setting an attribute to indicate which file should be used until they are all transferred, then removing the old uploader. That seems a little excessive.
Minimal to Possibly Zero Donwtime Procedure
In my opinion, the easiest and fastest way to accomplish what you want with almost no downtime is this: (I will assume that you will use AWS cloud, but similar procedure is applicable to any cloud service)
Figure out and setup your assets bucket, bucket policies etc for making the assets publicly accessible.
Using s3cmd (command line tool for interacting with S3) or a GUI app, copy entire assets folder from file system to the appropriate folder in S3.
In your app, setup carrierwave and update your models/uploaders for :fog storage.
Do not restart your application yet. Instead bring up rails console and for your models, check that the new assets URL is correct and accessible as planned. For example, for a video model with picture asset, you can check this way:
Video.first.picture.url
This will give you a full cloud URL based on the updated settings. Copy the URL and paste in a browser to make sure that you can get to it fine.
If this works for at least one instance of each model that has assets, you are good to restart your application.
Upon restart, all your assets are being served from cloud, and you didn't need any migrations or multiple uploaders in your models.
(Based on comment by #Frederick Cheung): Using s3cmd (or something similar) rsync or sync the assets folder from the filesystem to S3 to account for assets that were uploaded between steps 2 and 5, if any.
PS: If you need help setting up carrierwave for cloud storage, let me know.
I'd try the following steps:
Change the storage in the uploaders to :fog or what ever you want to use
Write a migration like rails g migration MigrateFiles to let carrierwave get the current files, process them and upload them to the cloud.
If your model looks like this:
class Video
mount_uploader :attachment, VideoUploader
end
The migration would look like this:
#videos = Video.all
#videos.each do |video|
video.remote_attachment_url = video.attachment_url
video.save
end
If you execute this migration the following should happen:
Carrierwave downloads each image because you specified a remote url for the attachment(the current location, like http://test.com/images/1.jpg) and saves it to the cloud because you changed that in the uploader.
Edit:
Since San pointed out this will not work directly you should maybe create an extra column first, run a migration to copy the current attachment_urls from all the videos into that column, change the uploader after that and run the above migration using the copied urls in that new column. With another migration just delete the column again. Not that clean and easy but done in some minutes.
When we use Heroku, most of people suggest to use cloudinary. Free and simple setup.
My case is when we use cloudinary service and need move into aws S3 for some reasons.
This is what i did with the uploader:
class AvatarUploader < CarrierWave::Uploader::Base
def self.set_storage
if ENV['UPLOADER_SERVICE'] == 'aws'
:fog
else
nil
end
end
if ENV['UPLOADER_SERVICE'] == 'aws'
include CarrierWave::MiniMagick
else
include Cloudinary::CarrierWave
end
storage set_storage
end
also, setup the rake task:
task :migrate_cloudinary_to_aws do
profile_image_old_url = []
Profile.where("picture IS NOT NULL").each do |profile_image|
profile_image_old_url << profile_image
end
ENV['UPLOADER_SERVICE'] = 'aws'
load("#{Rails.root}/app/uploaders/avatar_uploader.rb")
Profile.where("picture IS NOT NULL OR cover IS NOT NULL").each do |profile_image|
old_profile_image = profile_image_old_url.detect { |image| image.id == profile_image.id }
profile_image.remote_picture_url = old_profile_image.picture.url
profile_image.save
end
end
The trick is how to change the uploader provider by env variable. Good luck!
I have migrated the Carrier wave files to Amazon s3 with s3cmd and it works.
Here are the steps to follow:
Change the storage kind of the uploader to fog.
Create a bucket on Amazon s3 if you already dont have one.
Install s3cmd on the remote server sudo apt-get install s3cmd
Configure s3cmd s3cmd --configure.
You would need to enter public and secret key here, provided by Amazon.
Sync the files by this command s3cmd sync /path_to_your_files ://bucket_name/
Set this flag --acl-public to upload the file as public and avoid permission issues.
Restart your server
Notes:
sync will not duplicate your records. It will first check if the file is present on remote server or not.

Carrierwave/fog are uploading files to S3, but the app is still trying to use the local path to access images

I know this is a broad question, and I'm biting off a little more than I can chew for a first stab at a rails app, but here I am.
I tried to add an image upload/crop to a basic status app. It was working just fine uploading the images and cropping them with carrierwave, but as soon as I started using Fog to upload to S3, I ran into issues.
The image, and it's different sizes, appear to be ending up on S3 just fine, but the app is still trying to access the image as "/assets/uploads/entry/image/65/large_IMG_0035.jpg"
Locally, it just shows a broken image, but on Heroku it breaks the whole thing because
ActionView::Template::Error (uploads/entry/image/1/large_IMG_0035.jpg isn't precompiled
The heroku error makes sense to me because it shouldn't be there. I've combed through the app but don't know what's forcing this. I'll post any code anybody thinks will work? Thanks in advance!
Clarification:
Just to clarify, the images are uploading to S3 fine, the problem is how the app is trying to display the image_url
The app is using a local path in the asset pipeline, not the S3 path that it's actually uploading to.
I was having the same issue. In my Carrierwave Initializer I was setting host to s3.amazonaws.com but when I removed that line altogether urls started working.
I hope this helps you resolve your issue, I fought this for several hours!
I believe this issue is related to how you are accessing your image in your view.
If you have mounted an uploader on the field avatar in the following manner:
class User < ActiveRecord::Base
mount_uploader :avatar, AvatarUploader
end
You would access it in your ERB as follows:
<%= image_tag(#user.avatar_url) %>
I would also suggest watching the following Railscast on the topic.
http://railscasts.com/episodes/253-carrierwave-file-uploads
Re-reading issue, I bet it has to do with Carrierwave using Herkou.
Give this a glance and see if it helps.
https://github.com/jnicklas/carrierwave/wiki/How-to%3A-Make-Carrierwave-work-on-Heroku
I am not clear what exactly do you want to achieve.
But for now I have 2 ideas:
For assets host in CDN, you can take a look at this:
https://devcenter.heroku.com/articles/cdn-asset-host-rails31
If you want the images to be part of a model-relation, here's my rough idea:
Put the images path in a table column.
For further information about this you can browse carrierwave github site.(It has many docs and tutorial)

Is there a best directory to place image uploads on heroku?

I'm using carrierwave and I want to change the directory where images are stored.
Right now the image URL is /uploads/modelname/image/51/nameoffile.jpg
the store_dir in ImageUploader is
def store_dir
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
I definitely do not want the modelname to show
Is there an accepted ideal path where images should be stored on heroku?
Heroku doesn't allow uploads to their servers. You need to use another storage medium, like Amazon's S3.
I'm actually using Parse's (www.parse.com) API to store images on their solution. But it depends how you need access to your images.
You can upload files to the Heroku dyno filesystems but the filesystem are perishable and not shared among your dynos. Here's a Gist showing how to make Carrierwave store uploaded file in AWS S3 which is a better option: https://gist.github.com/cblunt/1303386
Here's a Heroku guide for accomplishing this with PaperClip: https://devcenter.heroku.com/articles/paperclip-s3

Rails] Images erased after a new commit on heroku

I'm using Carrierwave to upload images, and my app is on Heroku right now.
Images are uploaded successfully unless I push a new commit to heroku. Images that I uploaded before a push seem to be erased when a new commit comes in. Does anyone know the reason behind and how to fix this issue?
Update:
The problem becomes, using carrierwave on Heroku without a storage server like Amazon EC3. Heroku does not save files in public folder, where carrierwave uploads by default.
app/uploaders/image_uploader.rb:
def store_dir
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
And you need to change this to somewhere in the tmp folder in order to preserve your image files after each commit. I tried to change it to
"#{Rails.root}/tmp/uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
But it does not work. I checked on my local environment, and the image is stored in the right location successfully. So I guess if I just set the routes to the right location, this could work. Could someone help me with this?
Since Heroku does not allow storing static files (unless it's associated the application itself), you should make Carrierwave upload to a remote repository (e.g. Amazon S3) using fog. Everytime a user uploads a file, the file will be automatically uploaded to your S3 storage instead of Heroku.

Resources