Re-create image from hex code - ruby-on-rails

We created an app on Android that a user can upload an image. The image is send through a post request on a Rails application as a bitmap. An example of what I get is this.
I want to use this hex code string in order to re-create the image and then save it to my Rails app as I could do with Paperclip.
Any tip or guidance to the right direction would be really appreciated.

For me, this looks very much like a JPEG file (it starts with 0xFFD8FF), so you could just save the string to a file and you're done:
class UploadsController
def create
storage_path.open('w') {|f| f.write params[:file] }
head :ok
end
protected
def storage_path
#storage_path ||= Rails.root.join('data/uploads', current_user.id, Time.now.to_i.to_s(36) << '.jpg')
end
end
Of course, this assumes you have a current_user method identifying a logged-in user (e.g. from Devise), and you want to save the file locally in $RAILS_ROOT/data/uploads/$USER_ID/.
For production, I'd create an Upload (or Image (or Media)) model (belongs_to :user), and move the processing logic (i.e. find storage place, convert string to file, additional checks) into that class.

Related

Delayed job to check if a carrierwave_direct upload has been completed and attached to a model

I'm using carrierwave_direct and delayed_job with user uploaded images. The process is:
User drag & drops or selects a file.
File is uploaded to S3 directly using carrierwave_direct to do all the form signing etc.
User is presented with a dialog to crop their image, the key (from carrierwave_direct) is passed in through a hidden field. At this point no record exists in the database, but a file is stored on S3. Should the user now move away from the page, we will have an abandoned / unattached file on S3 that does not belong to any model.
If user completes the process, a record will be created and processing (cropping and creating several thumbnail versions) is done by a worker queue.
I had to make a delayed job that is put into the queue as soon as the file is uploaded. It will have a delay of several hours (or days). This job checks to see if the file exists, then checks through all recently created image records in the database to see if any are attached to that file. If not, it deletes the file.
Lastly, I wanted to make use of Carrierwave classes to handle all the Fog stuff, since I was using it anyway.
I could not find this anywhere, so here is my version. Leaving this here for people to come across in the future.
This is how I did it.
def new
# STEP 1: Show upload dialog that user can drop an image unto
#image = current_user.portfolio.images.new.images
# Status 201 returns an XML
#image.success_action_status = "201"
end
def crop
# STEP 2: A file is now on Amazon S3, but no record exists in the database yet. Make the user crop the image and enter a title.
# Meanwhile, also setup a delayed job that will later check if this step has been completed.
# Note: the crop view gets retrieved and inserted using ajax in images.js.coffee
#image = Image.new(key: params[:key])
Delayed::Job.enqueue ImageDeleteIfUnattachedJob.new(params[:key]), 0, 15.minute.from_now.getutc
render :partial => "images/crop.html.erb", :object => #image
end
ImageDeleteIfUnattachedJob = Struct.new(:key) do
def perform
# Do any of the images created in the last week match the key that was passed in?
# If not, the user probably went through the upload process, which then either went wrong or was cancelled.
unless Image.where("created_at > ?", 1.week.ago).order('created_at DESC').any? { |image| key == image.images.path }
# We spawn these to let Carrierwave handle the Fog stuff.
#uploader = ImageUploader.new
#storage = CarrierWave::Storage::Fog.new(#uploader)
#file = CarrierWave::Storage::Fog::File.new(#uploader, #storage, key)
if #file.exists?
# Indeed, the file is still there. Destroy it.
#file.delete
else
return true
end
end
end
def max_attempts
3
end
end

How to crop a temp image with Paperclip and store it in Amazon S3 in Rails 3

I'm having a great trouble trying to do some out-of-ordinary tricks with paperclip.
This is my situation:
My app users have an avatar image and my idea is to let them crop their avatars through Jcrop. My app is hosted in Heroku so that I must to upload the images to Amazon S3.
I've used this popular railscast to implement the cropping functionality but it requires to process two times the images. Here is where the problems started.
I think than a posible solution might be not process the images in the first time (when the user selectes an image) but do it the second. I've implemented this code in my controller:
def change_avatar
#user = current_user
paperclip_parameters = params[:user][:avatar] #first time call
if #temp_image_object.present? || params[:avatar].present?
if check_crop_params #second controller call
#user.avatar = File.new(#user.tmp_avatar_path) #overrides the
redirect_to #user, notice: t("messages.update.user.avatar") if #user.save
else #first controller call
#temp_path = #user.generate_temp_image(paperclip_parameters)
#user.tmp_avatar_path = #new_path #store the custom path to retrieve it in the second call
render :action => 'avatar_crop' if #user.save
end
end
end
def check_crop_params
!params[:user][:crop_x].blank? && !params[:user][:crop_y].blank? && !params[:user][:crop_w].blank? && !params[:user][:crop_h ].blank?
end
and in my user model:
#this method copies the original image tempfile when user upload the image to a custom path and returns the custom path
def generate_temp_image(paperclip_parameters)
uploaded_img_path = uploaded_img.tempfile.path
temp_dir = Rails.root.join('public/tmp')
Dir.mkdir(temp_dir) unless Dir.exists?(temp_dir)
FileUtils.cp(uploaded_img.tempfile, temp_dir)
new_path = uploaded_img_path
end
I also have the custom processor for jcrop, that takes the crop variables when proccessing the images.
When I upload an image (first controller call) the change_avatar method works well but when I crop the image (second controller call) the image isn't cropped, paperclip creates the image styles files but ignores the cropping I did.
Any ideas? what I should do?
I had forgotten an small detail.. looking in the server logs I realized that the paperclip process wasn't cropping the images, so I looked in my custom processor and I find that it depended on the crop parameters : crop_x, crop_y, crop_w, crop_h.
For some reason this parameters didn't reach the processor, so the images never were going to be cropped. All I had to do is to manually assign this params to the user vars, and it works!
The best strategy is direct-upload original upload to s3. This tutorial has an example talking about how to run the paperclip post-processing method after uploading to S3. It also discussed how to avoid the Heroku H12 error in case the post-processing takes too long on S3.
You may also check out this article. It has a demo code using paperclip and jcrop.

Multi step form with image uploader

I want to build 3 step user registration with avatar uploading on 2nd step. So i follow Ryan Bates's guide http://railscasts.com/episodes/217-multistep-forms . I'm using CarrierWave gem to handle uploads. But it seems like i can't store uploaded file info in user session (i'm getting can't dump File error). I use following technique in controller
if params[:user][:img_path]
#uploader = FirmImgUploader.new
#uploader.store!(params[:user][:img_path])
session[:img] = #uploader
params[:user].delete(:img_path)
end
It actually helps. But when i upload forbidden file type everything's crashes on this line
#uploader.store!(params[:user][:img_path])
with this error
CarrierWave::IntegrityError in UsersController#create
You are not allowed to upload "docx" files, allowed types: ["jpg", "jpeg", "gif", "png"]
instead of normal form validation error.
How can i solve this problem ? Thanks !
Actually I solved my problem. Here's working code for multistep forms with file uploading using carrierwave
if params[:user][:img_path]
#uploaded = params[:user][:img_path]
params[:user].delete(:img_path)
end
session[:user_data].deep_merge!(params[:user]) if params[:user]
#user = User.new(session[:user_data])
if #uploaded
# here how validation will work
#user.img_path = #uploaded
end
#user.current_stage = session[:register_stage]
if #user.valid?
if #user.last_stage?
#user.img_path = session[:img] if #user.last_stage?
#user.save
else
#user.next_stage
end
# now we can store carrierwave object in session
session[:img] = #user.img_path
session[:register_stage] = #user.current_stage
end
This may be a little late for the OP, but hopefully this helps someone. I needed to store an uploaded image in the user's session (again for a multi-step form), and I too started with Ryan's Railscast #217, but the app quickly evolved beyond that. Note that my environment was Rails 4 on Ruby 2, using Carrierwave and MiniMagick, as well as activerecord-session_store, which I'll explain below.
I believe the problem that both OP and I had was that we were trying to add all of the POST params to the user's session, but with a file upload, one of the params was an actual UploadedFile object, which is way to big for that. The approach described below is another solution to that problem.
Disclaimer: As is widely noted, it's not ideal to store complex objects in a user's session, better to store record identifiers or other identifier data (e.g. an image's path) and look up that data when it's needed. Two major reasons for this keeping the session and model/database data in sync (a non-trivial task), and the default Rails session store (using cookies) is limited to 4kb.
My Model (submission.rb):
class Submission < ActiveRecord::Base
mount_uploader :image_original, ImageUploader
# ...
end
Controller (submissions_controller.rb):
def create
# If the submission POST contains an image, set it as an instance variable,
# because we're going to remove it from the params
if params[:submission] && params[:submission][:image_original] && !params[:submission][:image_original].is_a?(String)
# Store the UploadedFile object as an instance variable
#image = params[:submission][:image_original]
# Remove the uploaded object from the submission POST params, since we
# don't want to merge the whole object into the user's session
params[:submission].delete(:image_original)
end
# Merge existing session with POST params
session[:submission_params].deep_merge!(params[:submission]) if params[:submission]
# Instantiate model from newly merged session/params
#submission = Submission.new(session[:submission_params])
# Increment the current step in the session form
#submission.current_step = session[:submission_step]
# ... other steps in the form
# After deep_merge, bring back the image
if #image
# This adds the image back to the Carrierwave mounted uploader (which
# re-runs any processing/versions specified in the uploader class):
#submission.image_original = #image
# The mounted uploader now has the image stored in the Carrierwave cache,
# and provides us with the cache identifier, which is what we will save
# in our session:
session[:submission_params][:image_original] = #submission.image_original_cache
session[:image_processed_cached] = #submission.image_original.url(:image_processed)
end
# ... other steps in the form
# If we're on the last step of the form, fetch the image and save the model
if #submission.last_step?
# Re-populate the Carrierwave uploader's cache with the cache identifier
# saved in the session
#submission.image_original_cache = session[:submission_params][:image_original]
# Save the model
#submission.save
# ... render/redirect_to ...
end
end
My uploader file was mostly stock with some custom processing.
Note: to beef up sessions, I'm using activerecord-session_store, which is a gem that was extracted from the Rails core in v4 that provides a database-backed session store (thus increasing the 4kb session limit). Follow the documentation for installation instructions, but in my case it was pretty quick and painless to set it and forget it. Note for high-traffic users: the leftover session records don't seem to be purged by the gem, so if you get enough traffic this table could potentially balloon to untold numbers of rows.

Rails 3.1 Deny editing other users images

I have a web app which uses Devise for authentication. It is a site which allows the user to upload images so the url would be /images/2. There is a separate image controller.
I have found that a user could edit an image they didn't upload by changing then URL, e.g. /images/4/edit. Is there a way to block them from editing other users images and only allowing them to edit their own?
The images controller, model, etc was created using rails g scaffold if that helps.
There are many different solutions for this, but the simplest is when running edit/update/destroy load the image from the current user instead of from all images:
def edit
#image = current_user.images.find( params[:id] )
end
def update
#image = current_user.images.find( params[:id] )
# do whatever has to be done
end
def destroy
#image = current_user.images.find( params[:id] )
# do whatever has to be done
end
Also, using scaffolds is a really bad practice, you should just write our own code, it's simpler, more productive and will lead you to understand how the framework is supposed to work.
You could also use nested controllers for this, but you would have to research a bit to understand how they work and why they might be a better solution.

pass file to paperclip on back end [rails]

I am using paperclip to attach multiple files to a Entry object
Class Asset < ActiveRecord::Base
belongs_to :entry
has_attached_file :asset, ...
Works wonderfully when user is uploading files via a nested form. However, I have certain instances in which a file is uploaded not via the input form but as a result of a flash audio recorder. Audio is recorded and uploaded asynchronously to my /tmp folder. So I end up with some files on my server that have not been uploaded via paperclip.
In these cases, I'd like to take the file and pass it to paperclip to be handled as if it were uploaded by a user via the input form. i.e. I need to utilize paperclip programmatically from within a controller.
How would you go about accomplishing this? Many thanks!
Ordinarily an uploaded file is passed to your controller as a File object in the params hash, which is then handed by the constructor, by way of attributes=, to the setter method created by Paperclip's has_attached_file--in your model's case Asset#asset= (might want to clarify those names a bit).
No, that's not an answer to your question, but it leads us to the answer. Knowing that you might realize that you can call that setter any time you want with a File as the parameter. E.g.:
class SomeController < ActionController::Base
def some_action
#some_asset = Asset.find 123 # (for example)
file_path = '/tmp/path/to/your/file'
file = File.open(file_path, 'r')
#some_asset.asset = file
#some_asset.save
end
end
Hope that's helpful!

Resources