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)
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 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.
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
In my app, I use carrierwave and fog to upload images to S3. In the site, people can select a size and download the photo. When they click download, the program will use image magick to resize the image according to the selected size and then send it to the user. It works find and it send the file. But the problem is when I open the image it's blank! Nothing in the file. When I test it in my local server it works but in production it won't. Here's the action which sends the download :
def download
#photo = Photo.friendly.find(params[:photo_id])
if #photo.free?
if params[:size].present?
size = Size.find(params[:size])
#photo.file.cache_stored_file!
img = Magick::Image.read(#photo.file.file.file).first
img.resize!(size.width, size.height)
filename = "#{size.width}x#{size.height}-#{#photo.file.file.filename}"
path = "#{Rails.root}/tempfreedownload/#{filename}"
File.write(path, '')
img.write path
File.open(path, 'r') do |f|
send_data f.read, type: 'image/png', filename: filename
end
File.delete(path)
img.destroy!
downloads = #photo.downloads + 1
#photo.update_attribute(:downloads, downloads)
else
end
else
render file: "#{Rails.root}/public/404.html", layout: false, status: 404
end
end
What might be the issue?
I'm having an invalid encoding error that doesn't let me save the image to a carrierwave uploader.
require 'rqrcode_png'
img = RQRCode::QRCode.new( 'test', :size => 4, :level => :h ).to_img.to_s
img.valid_encoding?
=> false
I'm not sure if this is what you're looking for, in my case I needed to associate the generated QR code with a Rails model using carrierwave, what I ended up doing was saving the image to a temp file, associating that file with the model and afterwards deleting the temp file, here's my code:
def generate_qr_code!
tmp_path = Rails.root.join('tmp', "some-filename.png")
tmp_file = RQRCode::QRCode.new(self.hash_value).to_img.resize(200,200).save(tmp_path)
# Stream is handed closed, we need to reopen it
File.open(tmp_file.path) do |file|
self.qr_code = file
end
File.delete(tmp_file.path)
self.save!
end