How to rotate image in S3 storage, I'm using Attachment_fu and Amazon S3 , I want to implement a feature that lets users edit existing images by rotating it 90 degrees. I was wondering if anyone had played with this, or had any ideas. I'm currently using rmagick
to rotate the image in Rails directory. But in AWS S3 storage cannot rotate the image.
require 'RMagick'
def rotate
photo = Photo.find(params[:id])
image = Magick::ImageList.new(photo.file)
image = image.rotate(90)
image.write(photo.file)
end
I'm using this gem:
gem 'pothoven-attachment_fu'
gem 'aws-s3'
gem 'rmagick'
I solved this problem.
def update_photo
require 'RMagick'
require 'base64'
# get the image data in Photo table
#photo = Photo.where(id: params[:photo_id]).first
# get image path in S3 url and encode to Base64 binary
img_url = #photo.s3_url()
img_uri = URI.parse(img_url)
base64_image = Base64.encode64(open(img_uri) { |io| io.read })
image_file_name = File.basename(img_uri.path,File.extname(img_uri.path))
filename_path = "#{Rails.root.to_s}/tmp/#{image_file_name}"
# create tempfile in rails root/tmp folder and decode the Base64 binary into image file, then processing RMagick to rotate the image into desire image rotation.
#tempfile = Tempfile.new(filename_path)
#tempfile.binmode
#tempfile.write Base64.decode64(base64_image)
#tempfile.close
# rotate image using RMagick
Magick::Image.read(img_uri).first.rotate!(params[:photo_degree].to_i).write(#tempfile.path)
# for security we want the actual content type, not just what was passed in
# get mime type of image and passed into ActionDispatch type
content_type = `file --mime -b #{#tempfile.path}`.split(";")[0]
# we will also add the extension ourselves based on the above
# if it's not gif/jpeg/png, it will fail the validation in the upload model
extension = content_type.match(/gif|jpeg|png/).to_s
extension = "jpg" if extension == "jpeg"
# append filename to prevent browser image caching
filename_path += "_#{params[:photo_degree]}"
filename_path += ".#{extension}" if extension
# passed the data into Rails core ActionDispatch instead of file input object
file = ActionDispatch::Http::UploadedFile.new({
tempfile: #tempfile,
type: content_type,
filename: filename_path
})
# delete the existing image
#photo.destroy
# create new image
img_rotate = Photo.new()
img_rotate.id = #photo.id
img_rotate.uploaded_data = file
img_rotate.save
ensure
clean_tempfile
end
def clean_tempfile
if #tempfile
#tempfile.close
#tempfile.unlink
end
end
Related
Unsupported color conversion request
*** WebP::EncoderError Exception: Cannot read input picture file
got this error on certain jpg file. while converting into jpg again from google does the job but why is this?
'''webp_path = "#{filename.ext}.webp"
# Encode (convert) image to webp format with passed options
WebP.encode(path, webp_path, options)
# HACK: Changing of this two instance variables is the only way
# I found to make CarrierWave save new file that was created
# by encoding original image.
#filename = webp_path.split('/').pop
#file = CarrierWave::SanitizedFile.new(
tempfile: webp_path,
filename: webp_path,
content_type: 'image/webp'
)'''
This is what I use successfully, obviously need MiniMagick installed on the dev computer and server:
class ImagesUploader < CarrierWave::Uploader::Base
include CarrierWave::MiniMagick
def store_dir
"uploads/story/#{model.created_at.strftime('%Y').to_s.underscore}/#{model.created_at.strftime('%m').to_s.underscore}/#{model.created_at.strftime('%d').to_s.underscore}"
end
def default_url(*args)
"/fallback.webp"
end
process :resize_to_fill => [1200, 677]
process convert_to_webp: [{ quality: 80 }]
version :preview do
process convert_to_webp: [{ quality: 80, resize_w: 470, resize_h: 265 }]
end
def extension_allowlist
%w(jpg jpeg gif png webp)
end
private
def convert_to_webp(options = {})
# Build path for new file
webp_path = "#{path}.webp"
# Encode (convert) image to webp format with passed options
WebP.encode(path, webp_path, options)
# HACK: Changing of this two instance variables is the only way
# I found to make CarrierWave save new file that was created
# by encoding original image.
#filename = webp_path.split('/').pop.split('.').first + '.webp'
#file = CarrierWave::SanitizedFile.new(
tempfile: webp_path,
filename: webp_path,
content_type: 'image/webp'
)
end
end
I want to create one API for image upload using CarrierWave with S3. Normal file upload is working but I want to create one API for upload image and return image URL, not need to the same name in the database table.
How can I do this?
You need to include the following gem in your gemfile,
gem 'aws-sdk', '~> 3'
And use below code in the controller (Using APIS)
require 'aws-sdk-s3'
class Api::V1::UploaderController < ApplicationController
def create
file = uploader_params[:image_url]
file_name = "#{file.original_filename}"
upload_file = file.tempfile
s3 = Aws::S3::Resource.new(region: ENV['AWS_REGION'],access_key_id: ENV['AWS_ACCESS_KEY'], secret_access_key: ENV['AWS_SECRET_ACCESS_KEY'] )
obj = s3.bucket(ENV['AWS_BUCKET_NAME']).object(file_name)
obj.upload_file(upload_file, { acl: 'public-read' })
success(data: obj.public_url, status: 200)
end
end
I just need to get the url of my image (base64) that just uploaded to Rackspace server via Carrierwave.
This is my controller now.
def update_with_image
user = current_user
uploader = PictureUploader.new
uploader.store!(user_update_params[:profile_image]) // base64 image like this 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2w...'
// How can i update user_update_params[:profile_image] with the successfully uploaded profile_image url?
if user.update_attributes(user_update_params)
# Handle a successful update.
render json: user, status: 200 ,serializer: UserSerializer
else
render json: { errors: user.errors }, status: 422
end
end
So after uploader.store!(user_update_params[:profile_image]) how can i get the url of that file?
Thanks!
You mean this ?
uploader = AvatarUploader.new
uploader.store!(my_file) # size: 1024x768
uploader.url # => '/url/to/my_file.png' # size: 800x600
uploader.thumb.url # => '/url/to/thumb_my_file.png' # size: 200x200
calling url method on the uploader should get you the URL. github
Update: Quoting from carrier wave GitHub
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.
config.asset_host = "http://c000000.cdn.rackspacecloud.com"
In your uploader, set the storage to :fog
class AvatarUploader < CarrierWave::Uploader::Base
storage :fog
end
That's it! You can still use the CarrierWave::Uploader#url method to return the url to the file on Rackspace Cloud Files.
I have a Rails 3.2 app that uses gem 'wicked_pdf', and gem 'combine_pdf'.
They both work and I can create PDFs which get emailed.
But, I have run into a situation where the email would be too big.
So, I'm trying to save the created pdf to Amazon S3. The app already has the gem 'aws-sdk'.
This is my code:
def self.saveallpdf
#costprojects = Costproject.where("client_id = 2")
pdf = CombinePDF.new
#costprojects.each do |costproject|
#costproject = costproject
controller = CostprojectsController.new
controller.instance_variable_set(:"#costproject", #costproject)
pdf2 = controller.render_to_string(pdf: "Captital Projects.pdf",
template: "costprojects/viewproject",
encoding: "UTF-8")
pdf << CombinePDF.parse(pdf2)
end
#s3 = AWS::S3.new
#bucket = #s3.buckets['ndeavor3-pdf']
#obj = #bucket.objects['filename'].write(pdf, acl: :public_read)
end
The error I'm getting is:
:data must be provided as a String, Pathname, File, or an object that responds to #read and #eof?
/app/vendor/bundle/ruby/1.9.1/gems/aws-sdk-1.8.3.1/lib/aws/s3/data_options.rb:125:in `validate_data!'
/app/vendor/bundle/ruby/1.9.1/gems/aws-sdk-1.8.3.1/lib/aws/s3/data_options.rb:32:in `compute_write_options'
/app/vendor/bundle/ruby/1.9.1/gems/aws-sdk-1.8.3.1/lib/aws/s3/s3_object.rb:594:in `write'
/app/app/models/costproject.rb:167:in `saveallpdf
'
I guess was-sdk doesn't like the "pdf" as the file??
PS - I can email the "pdf" - if it was smaller in size.
Thanks for your help!
At this point in time:
#obj = #bucket.objects['filename'].write(pdf, acl: :public_read)
pdf is a CombinePDF object and not a File, String, or Pathname
pdf.to_s might work, or you will have to create a new file from the CombinePDF object
File.new(CombinePDF) # pseduo code only
I have an Image model that requires me to have some data set up before I can save the image, because I determine where to upload the image to and what to name it depending on some data from the model. Right now I'm trying to do:
# ImagesController sets up data
img = Image.new
img.imageable = user
phashion_img = Phashion::Image.new(file.path)
img.image_hash = phashion_img.fingerprint.to_s
img.batch = img.latest_batch + 1
img.extension = 'jpg'
img.height = 640
img.width = 640
# ImageUploader uses it
version :original do
def full_filename
"#{model.image_hash}/orig/#{alnum_encode(model.imageable.id)}.#{model.extension}"
end
end
I've run into an issue trying to pass the uploaded image into the uploader.
img.uploader.store! file gives me the error wrong number of arguments (1 for 0)
img.uploader.store! file.tempfile gives me You are not allowed to upload "" files, allowed types: jpg, jpeg, gif, png
For reference, file is:
#<ActionDispatch::Http::UploadedFile:0x00000109ccdb70 #tempfile=#<Tempfile:/var/folders/lx/xk8vzr4s0fdd_m5w0syftfl80000gn/T/RackMultipart20140723-28731-1b5weu8>, #original_filename="20140522_164844.jpg", #content_type="image/jpeg", #headers="Content-Disposition: form-data; name=\"user[images_attributes][0][uploader]\"; filename=\"20140522_164844.jpg\"\r\nContent-Type: image/jpeg\r\n">
Which would make file.tempfile:
#<Tempfile:/var/folders/lx/xk8vzr4s0fdd_m5w0syftfl80000gn/T/RackMultipart20140723-28731-1b5weu8>
Is there any way I could pass either of these objects, ActionDispatch::Http::UploadedFile or Tempfile, to the Carrierwave uploader? Some transformation I can do to them so that the uploader can accept one of them?
Since my use case is so far off from what is intended with CarrierWave, or any of the other image upload gems out there, I ended up rolling my own solution using the AWS-SDK gem to upload to S3. It's pretty specific, but I'm posting the gist of it on the off chance that it helps someone else.
# Image processing
magick = Magick::Image.from_blob( file.read )[0]
if !["JPG", "JPEG", "GIF", "PNG"].include? magick.format
# Invalid format specified
end
orig = Image.new
orig.imageable = user
phashion_img = Phashion::Image.new(file.path)
orig.image_hash = phashion_img.fingerprint.to_s
orig.batch = orig.latest_batch + 1
orig.extension = magick.format.downcase
filename = "#{orig.image_hash}/orig/#{Alnum.encode(orig.imageable_id)}.#{orig.extension}"
orig.height = magick.rows
orig.width = magick.columns
orig.size = 'orig'
# Upload to S3
s3 = AWS::S3.new
uploaded_name = Rails.root.to_s+filename.gsub('/', '_')
magick.write( uploaded_name ) # Save file to filesystem so I can upload to S3
s3.buckets[bucket_name].objects[filename].write( open(uploaded_name), :acl => :public_read )
FileUtils.rm uploaded_name # Remove so it's not taking up space
# Crop
cropped = magick.crop(x.to_i, y.to_i, w.to_i, h.to_i, true) # Discard offset data to avoid borders
# Create Profile
# Resize_from is my own custom function to copy everything
# but the size into another object and resize the image
resize_from(s3, orig, cropped, 640, 640)
# Create Grid
resize_from(s3, orig, cropped, 220, 220)
# Create Thumbnail
resize_from(s3, orig, cropped, 72, 72)