Carrierwave and Amazon S3: retrieve image - ruby-on-rails

I have a problem with S3 and CarrierWave:
I have a pseudo-form that uploads data and files, I wrote "pseudo" because it's an ajax form so data is sent with jquery to rails with a POST request. Files cannot be uploaded in this way...so I have a popup windows that upload files to rails, I save in the session the reference to the uploaded files and when the ajax request uploads the rest of the form, I link the files uploaded to the rest of the data.
With storage :file it works without any problems, when i receive the file I do:
uploader = ImgObjUploader.new
uploader.store!(params[:image_form][:image])
session["image"] = uploader.url
and then when I get the rest of the data:
if (session[:image] != nil) then
obj.image = File.open(session[:image])
end
And my model is:
mount_uploader :image, ImgObjUploader
This code work without any problems, for amazon s3 I switched to:
uploader = ImgObjUploader.new
uploader.retrieve_from_store!(session[:image])
puts uploader
#obj.image = uploader
obj.image = uploader.url
but it doesn't work...I didn't receive an error but I don't have the image saved inside obj object. Puts uploader prints the url of amazon S3.
Anyone can help me?
Thank You.

Related

Best way to transload a file in rails (from HTTP URL to S3)

In Rails, what is the best (ie. efficient, elegant) way to download a file from a public HTTP url, upload to Amazon S3 and delete the file in the server.
I am using Heroku so I have the additional restriction of a ephemeral file system.
If reading into memory isn't an option.
You can use the /tmp directory. As long as this is in the same process using that shouldn't be a problem.
The answer is to read your image into memory instead of storing it on the filesystem. Here's an example.
s3 = Aws::S3::Resource.new
obj = s3.bucket(your_bucket_name).object(your_object_key)
s3_put_url = URI.parse(obj.presigned_url(:put))
image_url = 'http://www.google.com/google.jpg'
image_file = open(image_url).read
Net::HTTP.start(s3_put_url.host) { |http| http.send_request('PUT', s3_put_url.request_uri, image_file); }
# Let's get the URL
s3.bucket(your_bucket_name).object(your_object_key).presigned_url(:get)

Save a picture in S3 from a temporary URL

I am developing a website on ruby on rails where users can upload pictures thanks to paperclip, it is stored in amazon S3. After, they can modify pictures thanks to aviary. But when i want to save the new pictures, aviary just gave me an temporary URL where i can get my modified picture.
Does paperclip can do it ? I don't think it can save an picture from an URL and store it to S3 ?
I've searched for a week now, and i don't know the best way to do it. I've read about filepicker, but the account to store data in S3 files isn't free ...
Finally i've heard about this s3 https://github.com/qoobaa/s3, but i don't understand how to use it. I have installed gem s3, but when i set require 's3' , it is not recognize.
What is the best to do?
Why don't you pass the URL that Aviary generates to your server and upload the new photo from there? The code below does that in Python/Django:
#login_required
#csrf_exempt
def upload_from_url(request):
origin_url = request.POST.get("origin_url")
name = request.POST.get("name")
try:
conn = boto.connect_s3(settings.AWS_ACCESS_KEY_ID, settings.AWS_SECRET_ACCESS_KEY)
bucket_name = settings.AWS_UGC_STORAGE_BUCKET_NAME
bucket = conn.get_bucket(bucket_name)
k = Key(bucket)
k.key = name
file_object = urllib2.urlopen(origin_url)
fp = StringIO.StringIO(file_object.read())
k.set_contents_from_file(fp)
return HttpResponse("Success")
except Exception, e:
return HttpResponse(e, mimetype='application/javascript')
Hope this helps.
Paperclip has matured a lot since this question was answered. If you want to save files by passing a URL, as of Paperclip v3.1.4, you can just assign the URL to your Paperclip attachment attribute.
Let's say I have a class User and my attachment is called avatar. We'll have the following in our User model:
has_attached_file :avatar
# Validate the attached image is image/jpg, image/png, etc
# This is required by later releases of Paperclip
validates_attachment_content_type :avatar, :content_type => /\Aimage\/.*\Z/
In our view, we can define a hidden field that will accept the temporary URL received from Aviary:
= f.hidden_field :avatar, id: 'avatar'
We can set the value of this hidden field with the Aviary onSave callback:
var featherEditor = new Aviary.Feather({
apiKey: '#{ENV['AVIARY_KEY']}',
onSave: function(imageID, newURL) {
var img = document.getElementById(imageID);
img.src = newURL;
var avatar = document.getElementById('avatar');
avatar.value = newURL;
featherEditor.close();
}
});
Within onSave, you can use AJAX to update the User object, use jQuery's .submit() to submit the form, or let the user submit it when they want.

Rails: save file from URL and save it to Amazon S3

What's the more straightforward way to download a file from given URL and uploading it immediately to Amazon S3 (+ save into database some information about the file, like name, size etc)?
Right now, I am not using Paperclip neither Carrierwave.
Thank you
Straightforward:
require 'open-uri'
require 's3'
amazon = S3::Service.new(access_key_id: 'KEY', secret_access_key: 'KEY')
bucket = amazon.buckets.find('image_storage')
url = 'http://www.example.com/url'
download = open(url)
file = bucket.objects.build('image.png')
file.content = (File.read download)
if file.save
# Make a new ActiveRecord::Base class for this
LogFile.create(size: download.size, type: download.type, name: url)
end
https://github.com/qoobaa/s3

Amazon S3 upload Image from Rails

I'm uploading the image using aws-s3 gem, it uploads perfectly and give me the url. Image is in PNG format but when I hit the public url in my browser, it starts downloading, but I don't want to download it, I want to see the image in browser.
Please tell me if you know which parameter in the following request should be added for this or what change is required to achieve this?
AWS::S3::S3Object.store(base_name,open(local_file),bucket,:content_type => mime_type,:access => :public_read,:authenticated => false)
s3_url = AWS::S3::S3Object.url_for(params[:name],bucket)[/[^?]+/]
Check :content_type => mime_type if mime_type is correct.

Using Send_File to a Remote Source (Ruby on Rails)

In my app, I have a requirement that is stumping me.
I have a file stored in S3, and when a user clicks on a link in my app, I log in the DB they've clicked the link, decrease their 'download credit' allowance by one and then I want to prompt the file for download.
I don't simply want to redirect the user to the file because it's stored in S3 and I don't want them to have the link of the source file (so that I can maintain integrity and access)
It looks like send_file() wont work with a remote source file, anyone recommend a gem or suitable code which will do this?
You would need to stream the file content to the user while reading it from the S3 bucket/object.
If you use the AWS::S3 library something like this may work:
send_file_headers!( :length=>S3Object.about(<s3 object>, <s3 bucket>)["content-length"], :filename=><the filename> )
render :status => 200, :text => Proc.new { |response, output|
S3Object.stream(<s3 object>, <s3 bucket>) do |chunk|
output.write chunk
end
}
This code is mostly copied form the send_file code which by itself works only for local files or file-like objects
N.B. I would anyhow advise against serving the file from the rails process itself. If possible/acceptable for your use case I'd use an authenticated GET to serve the private data from the bucket.
Using an authenticated GET you can keep the bucket and its objects private, while allowing temporary permission to read a specific object content by crafting a URL that includes an authentication signature token. The user is simply redirected to the authenticated URL, and the token can be made valid for just a few minutes.
Using the above mentioned AWS::S3 you can obtain an authenticated GET url in this way:
time_of_exipry = Time.now + 2.minutes
S3Object.url_for(<s3 object>, <s3 bucket>,
:expires => time_of_exipry)
Full image download method using temp file (tested rails 3.2):
def download
#image = Image.find(params[:image_id])
open(#image.url) {|img|
tmpfile = Tempfile.new("download.jpg")
File.open(tmpfile.path, 'wb') do |f|
f.write img.read
end
send_file tmpfile.path, :filename => "great-image.jpg"
}
end
You can read the file from S3 and write it locally to a non-public directory, then use X-Sendfile (apache) or X-Accel-Redirect (nginx) to serve the content.
For nginx you would include something like the following in your config:
location /private {
internal;
alias /path/to/private/directory/;
}
Then in your rails controller, you do the following:
response.headers['Content-Type'] = your_content_type
response.headers['Content-Disposition'] = "attachment; filename=#{your_file_name}"
response.headers['Cache-Control'] = "private"
response.headers['X-Accel-Redirect'] = path_to_your_file
render :nothing=>true
A good writeup of the process is here

Resources