Best practice for file upload before object is created? - ruby-on-rails

I got a form that has several steps and the first one is to upload an image (via carrierwave) which is supposed to happen in the background. So the server is receiving a file for an object that doesn't exist yet. So I'm wondering what's the best practice for this case?
I thought of two approaches:
Make the image file a model that is created and returning the image ID upon upload completed.
Create the object, having validations disabled and only the image saved returning the object ID and update it afterwards with validations on.
I'm leaning towards no. 1 but maybe one of you guys have an even better idea? Not happy creating a new model just for the image.

carrierwave is a quality gem that will help you with your purposes. It doesn't require you to create a new model for image, rather you simply add an image field to one of your DB tables in schema.rb.
Relevant Tutorial

Related

Rails uploads for new object

When creating a new record (rails default new action), the object id is nil b/c it's unpersisted in the db.
When uploading objects, they're usually tied to an id (using paperclip).
When my user clicks on new, and I want to provide a dropzone.js area for ajax upload, how do I tie that image/file to the post object when it has no id?
If they discard or exit the browser, I would have orphaned temp images/files...
I'm having trouble connecting the dots between an object upload for a new record.
Can someone help me work out the controller logic for this? I can do it pretty easily in a separate action after the object is created, but not before.
I made a Gem to solve this kind of problem.
It works on top of paperclip and is not intrusive. To enable the functionality, you need to replace paperclip's has_attached_file with has_attached_upload.
Using Rails Pallet gem...
First, you need to upload the file to your server performing POST /uploads with file attribute. The response will give you an identifier related to that file.
Then, sending the identifier, you can update your own record. The gem will copy the file to your record after that.
This way, you can upload files before persisting your record.
You can see a full example on gem's README

Paperclip: copy_to_local_file called upon every update (of unrelated attributes)

I'm using paperclip 4.1.0 with Amazon S3.
I was wondering why requests were so slow and found that "copy_to_local_file" is called whenever I am updating attributes of a model with an attachment, even if it's just one attribute unrelated to the attachment (in my case, a cache_count, which means that every time someone votes an instance up, the attachment is downloaded locally!).
I understand it is used in case a rollback is required, but it seems overkill when the attribute isn't directly related to the attachment.
Am I using paperclip in the wrong way or is it something that could be improved ?
Thanks for your help
Just my 2 cents:
the attachment gets downloaded locally only after ActiveRecord::Base#save is called.
Would calling the 'base#save' in a cron on a daily basis instead help with the load?
Otherwise, either remove the calling of method copy_to_local_file if possible
or editing the source of paperclip's method of copy_to_local_file(style, local_dest_path) to exclude the downloading of attachment.
This was an issue with paperclip, it's fixed on the master branch!

AJAX file upload on creation of an object in rails with paperclip

I have a head aching problem that I can't seem to find an easy solution to.
I have a couple of models, each with an image attachment, that belongs to a user. I have made a very nice ajax file upload and image cropping form, but there is a problem. Everything works fine when I am editing objects that is already in the database but when I upload a file as I create a new object it doesn't. The thing is, to be able to upload and save the image, the object already has to be in the database. I have found two possible solutions to this problem but none of them will work properly.
The first one is to create the object in the database in the new action and redirect to edit action. The pros is that it is a very simple fix. The cons is that the objects will show up in the list with previously created ones even if the user canceled or never submitted the form, which is very confusing.
The second possible solution is to lift out the attachment fields of the model to a separate model. On creation I would then only need to create an attachment object. If the user canceled it would leave the attachment orphaned, but that is probably okay as the orphans could be cleared periodically. The problem with this is that I can't find a way to prevent users from hijacking the orphaned images, or any other image for that sake. Unless I cant solve this problem I'm stuck.
I'm all out of ideas and would really need some help on this one.
Thanks, godisemo
EDIT:
I was probably unclear. In my form it is possible to upload an image. The image is uploaded instantly to the server with javascript, before the form is submitted. The reason is that I want to allow the users to crop the image. This is no problem wen working with existing objects but it is when creating new ones, as I tried to explain earlier.
I've never had to have the model already in the db for paperclip to work.
One thing you can try though is the following. I don't know what your model is called but let's say User has an image. Make your new form so that all of your user fields are passed in the params[:user] var, but then make your image upload file separate from params[:user], say params[:my_image].
Then in your controller validate and save the user, then after user.save, attach the image.
I solved the problem now, with a completly different approach. Instead of thinking databases, objects and models I solved it using file system and temporary files. When the image is uploaded it is processed by paperclip, I then move the generated images to a folder where I have control over them.
I based my solution on a really great article which you can find here, http://ryantownsend.co.uk/articles/storing-paperclip-file-uploads-when-validation-fails.html

Rails3, S3, Paperclip Attachment as it's own model?

So, I'm working on an app where users can upload and manage photos with a bunch of industry specific metadata attached to them.
The Photo model has all this metadata in it, and I'm using Paperclip to attach the actual image file to the model and store the images on Amazon S3.
The user interaction currently works like this:
A user clicks "Add Photo" and is taken to the "New Photo" page where he is presented a form.
The first thing on the form is a file chooser. The user selects a file.
Beneath this are several different fields of metadata for the user to fill out, so the user fills these out.
The user hits submit, the file uploads and a new Photo object is created, the user is redirected to a different page.
So, the obvious improvement that I'd like to make is for the photo to actually upload at the beginning of this process so that there isn't such a noticeable delay between hitting submit and being redirected to the next page. It would also be nice to be able to show the user a thumbnail preview of their photo once it's done uploading, so that they can see the photo they're putting in metadata about as they fill in the form.
I figure I could make that happen if I split the image file into its own model, but I'd then be referring to the images like so:
#photo.attachment.file.url instead of the simpler #photo.file.url that I use now. I'd rather not "nest it" more deeply than I have to.
Also, splitting it into two models raises the issue of managing orphans, which I currently don't have to deal with.
So my questions are:
Is there a good way - preferably not using Flash - to create this asynchronous upload behavior without splitting into two models, OR --
If I must split the metadata and the file into two models, is there a way to get Paperclip to treat an attachment as its own model so that I can access it using <modelname>.<paperclip_method> instead of <model_name>.<attachment_attribute>.<paperclip_method>?
I know that's a big question, so thank you very much in advance for your help!
I'd recommend that you replace the new action with an edit action (you can auto-create a photo model when the user selects the action. This way, you can use an AJAX file upload (supported in some modern browsers) with a Flash fallback for doing the upload, and also edit the metadata. For doing the upload, try looking at plupload or uploadify.
What's wrong with simply defining Photo#url like so?
class Photo
def url(*args)
attachment.url(*args)
end
end
No need to get fancy here.
After a week of experimentation I just thought I'd post what I finally did:
I did in fact split the photo into two models, because I ended up having to create empty records with any approach I tried. I found it was easier in the end to have two seperate models because:
It made it easier to work within Rails convention (use the standard REST actions for the second model to handle asynchronous updates, rather than having to add several custom actions to the parent model).
No matter which option I tried I ended up having orphan records as a possibility. I found it was easier to have a parent object which does not ever save unless valid (the Photo model in my case), and to have attachments which may be orphans. The attachments are never called directly anywhere in the app, so there's no risk of accidentally having an empty record being pulled up, and no need to set a default scope or something in order to only show "valid" photos. Cleaning up orphans is pretty easy, just do Attachment.where( :parent_id => nil ) and delete all those.
I can maintain the previous code by simply delegating attachment to photo. See this answer on another question.
I hope this saves others some trouble down the road. If you need Ajax functionality with attachments it is best to make them their own model. Also, for adding ajax file upload to forms, check out https://github.com/formasfunction/remotipart. This saved my app.
Just wondering, are you not setting the dependent clause on your association? For example,
Class Photo < ActiveRecord::Base
:has_one :attachment, :dependent => :destroy
end
This will prevent orphan records, as anytime a destroy method is called on Photo, it'll first do Photo.attachment.destroy. It works with :has_many as well.
Basically, you're splitting up the creation of the Photo object into two parts: uploading the photo, and adding the meta-data. In a non-AJAX site, this would be two separate steps on two separate pages.
What I would do is allow the AJAX upload to instantiate a Photo object, and save it (with the uploaded photo). I would then have the AJAX code return a token to the site that corresponds to a session variable to let the form know the record for the photo has already been created. When the user submits the rest of the form, if the token is present, it will know to populate the data on the already created photo object. If the token is not present, it will know it needs to create the Photo object from scratch.

initializing copy of photo in a different model with paperclip plugin for rails

Here's my question. I have a user model with one attached avatar. This model has many personal photos (with accepts_nested_attributes_for).
I want to be able to initialize a personal photo automatically after saving a user object with whatever the user avatar turns out to be. So say Bob uploads his avatar, bob will automatically have one personal photo (with the correct different paperclip styles) generated from the avatar image.
I'm not really sure how to go about doing this. Would I put it in my controller or user an after_save hook in the model? I'm using Paperclip with db storage so it would be good if somehow during save this was initialized so I don't have to pull it back out...Maybe I could use a hidden form field?
Honestly... I'm not sure I'd recommend this course of action. Many people upload avatars that aren't photos. If you do this, certainly you should give the user the option to delete the photo without also deleting their avatar at the same time. This means that you need to duplicate the attachment. To do that, you have to hook into the after_avatar_post_process callback. In this callback, create a new personal photo object. On the photo model's photo attachment, call something like personal_photo.photo.assign(avatar.path). I think that should work, but I haven't tried it. My main worry is that the assign call might not create a new location for the attachment. I think it does, but I don't know absolutely for sure. At the very least, it's close to what you need to do and should get you moving in the right direction.
Paperclip API

Resources