Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Fine Uploader with Rails form

I've recently installed Fine Uploader to our Rails app. I've read documentation and experimented a little with it, but I don't seem to understand how this thing actually works, and because of that, I'm having a lot of problems implementing it.

What I did: Installed it (two ways, one "classical" and the second using the fineuploader gem, which seems to do the same).

Created a coffee file containing this.

$ ->
  uploader = new (qq.FineUploader)(
    debug: true
    element: document.getElementById('fine-uploader')
    request: endpoint: '/uploads')
    template: 'test-template'

And that creates an "upload a file" button (which of course does not work, cause there is no configuration to handle this on the server site), but I would like to have this button in the simple-form input field.

Also, templating does not work, I don't really understands why.

Unlucky, documentation lack help for Rails.

I'm open for changing this library to something else, as long as it contains the features that I need which are:

  • pausing and resuming download
  • file chunking (in case of failure, user won't be forced to start over)
  • progress bar (not vital)
like image 813
ZebThan Avatar asked Jan 06 '23 00:01

ZebThan


1 Answers

I've managed to make it work, and I have some knowledge about how it works, so I'm going to share this knowledge. But I want to strongly point out, that I have figured this on my own, so there might be flaws in what I'm providing. Use your knowledge and experience, and if you think something can be done better, you may be right.

First of all, download the library using npm. I have found two gems for it, but don't use them. At the time I'm writing this, those gems are outdated, and won't be working. Github links:

github.com/mezis/fineuploader-rails
github.com/zakgrant/fine-uploader-rails

If you happened to download those, you will have to reinstall library.

After that, you will be creating the javascript. I've created a file called "fine-uploader.coffee", and put there something like this:

$ ->
  uploader = new (qq.FineUploader)(
    element: document.getElementById('fine-uploader')
    request:
      endpoint: '/upload'
      params:
        authenticity_token: $('#fine-uploader').data('authenticity-token')
    template: 'template-name'
    chunking:
      enabled: true
      mandatory: true
      success:
        endpoint: "/upload/finish"
)

I'm not going to describe what is what, because all of this can be found in the docs, instead I'll focus on what is important for us. In the "request" you have an endpoint, for this case, we will need to add it to our routes, like this.

post 'upload', to: 'uploads#create'

Then, we will need to create our controller. As you can see, I've created dedicated controller for handling uploads, and I recommend you doing so.

In the uploads controller we have this action:

def create
    file = params[:qqfile]

    #code that does whatever you need

    respond_to do |format|
       format.json {
          render json: { success: true }
       }
    end
end

In your controller, you will have some params (you can check all of them in the docs), but the one that is important to you is qqfile. This will be the file, that was posted. You can do whatever you want with it. I was using carrierwave to save and process it, and I guess this does not require further description (as you can find it in the carrierwave dosc, also, you maybe using something else, like refile).

You will also have to add a parameter with token, you can see it in the 7th line of the javascript file. I recommend reading about CSRF tokens.

Important thing is, that if you chosen (like me) the chunking option, this will NOT be the whole file, only the part of it. You can find bigger tutorials on how to handle parted files, but what you need to pay attention for is the qquuid that you will be send in params. This id identifies the file that is being uploaded, and you will need it to determine to what file uploaded part belongs, so you don't mix two files up. Every part is being send by separate post request, so I recommend adding the column like qquuid to the model that is handling the files. For example:

Attachment.new(params[:qqfile], params[:qquuid])

After you save all the files, you can combine them (check the ruby File class to learn how to).

Attachment.where(qquuid: params[:qquuid]).combine_them_all

The above line can be put either in the create action at the end of it, with the condition stating that this is the last part (there are params with the total part number and the index number of the current part, that can be uset to determine that), or in the "finish" action, that you can use if you do

chunking:
    success:
        endpoint: "/upload/finish"

You will have to create a route (same way as shown above) and the action. This action will be triggered after the last part was send. If the file was too small to be parted, this will NOT get triggered, unless you set mandatory to "true".

The last thing, is the json respond. That part is simple, fine uploader expects you to respond (with json) that you received the file and everything went smoothly. If it won't get it, it will assume, something went wrong (you can read more about expected json in the uploader docs). What matters is that in case of failure, it will try to send failed part again (if autoRetry is set to true).

Now, for the view (haml):

#fine-uploader{"data- authenticity-token" => form_authenticity_token}
%script#template-name
    Here goes the template

More about templating you can read in docs, but important thing is, don't use rails form (or simple form) for that. There is a way of connecting forms with fine uploader, but I wasn't able to make it work, and I've had no need of using the form, but if you need it, you can create a hidden field with the generated token, to identify it, and use it instead of qquuid to identify the files. You can provide it in params section the same way, you have provided CSRF token, and use it in the "create" action in the uploads controller.

And that's it. I hope it will help someone.

EDIT: There is an example app made by user Panczo posted in the comments

like image 132
ZebThan Avatar answered Jan 16 '23 16:01

ZebThan