ncuesta / dropzonejs-rails

Spice your Rails apps with some Dropzone sugar!
http://rubygems.org/gems/dropzonejs-rails
MIT License
312 stars 56 forks source link

uploadMultiple: true #34

Closed cielmik closed 8 years ago

cielmik commented 8 years ago

I'm currently having a problem with multiple uploads in one post request.

this is my current dropzone settings:

var dropzone = new Dropzone (".dropzone", { maxFiles: 10, maxFilesize: 5, paramName: "pictures[]", addRemoveLinks: true, autoProcessQueue: false, uploadMultiple: false, parallelUploads: 10 });

$(".submit").click(function (e) { e.preventDefault(); e.stopPropagation(); dropzone.processQueue(); });

My issue is that setting uploadMultiple to true will not let me upload anything(returns nil).

Setting uploadMultiple: false works, but each file is one post request. e.g. 3 photos is album_id: 1,album_id: 2,album_id: 3. I wanted 3 photos be in one post request so that they will all belong to the same album_id.

Is uploadMultiple:true not working with rails?

ncuesta commented 8 years ago

Hi @cielmik, thanks for using this gem.

That should work with Rails.. I'm having a hunch it may be an issue with your controller. Could you please post the relevant portions of your PicturesController (or whichever name it has)?

cielmik commented 8 years ago

Hi @ncuesta, thank you very much for trying to solve my problem

here is my controller:

  def create
    @album = Album.new(album_params)
    respond_to do |format|
      if @album.save
        save_pictures
        format.html { redirect_to root_path, notice: 'Album was successfully created.' }
        format.json { render json: @album }
      else
        flash[:danger] = "Album has not been saved."
        render 'new'
      end
    end
  end

    private
    def save_pictures
      if params[:pictures]
        params[:pictures].each { |image|
          @album.pictures.create(image: image)
        }
      end
    end

my view (slim)
= simple_form_for @album, :html => { :class => 'dropzone', id: "my-dropzone", multipart: true } do |f|
  = f.input :name, class: "form-control"
  .fallback
    = file_field_tag "pictures[]", multiple: true
  = f.button "submit", class: "btn btn-primary submit"

This is the output I get when using uploadMultiple: true

SQL (5.0ms) INSERT INTO "pictures" ("image", "album_id", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["image", nil], ["album_id", 44], ["created_at", "2016-03-03 02:04:31.756316"], ["updated_at", "2016-03-03 02:04:31.756316"]]

Thank you very much.

cielmik commented 8 years ago

Okay I solved the problem. Solution: Per the documentation: http://www.dropzonejs.com/#config-paramName

In uploadMultiple: True, the paramName will be appended with a [n]

So in this example you are trying to upload 3 images:

html: <input multiple="multiple" class="file optional" type="file" name="pictures[]" id="album_images" />

Each additional image will append a [n] in name = pictures[][n+1]. Output for 3 images will be: name="pictures[][0]" --> Image 1 name="pictures[][1]" --> Image 2 name="pictures[][2]" --> Image 3

What you want is: name="pictures[]" --> Image 1 name="pictures[]" --> Image 2 name="pictures[]" --> Image 3

The solution is to remove the appended [] in dropzone.js.

Just go to this line in dropzone.js: return "" + this.options.paramName + (this.options.uploadMultiple ? "[" + n + "]" : "")

and delete (this.options.uploadMultiple ? "[" + n + "]" : "")

so it will just be:

return "" + this.options.paramName

ncuesta commented 8 years ago

Hi @cielmik sorry for the late response.

I don't think you would want to modify Dropzone's source code. Specially for this kind of situations, where the default behavior is pretty close to what you want to achieve.

Maybe you could accept the parameters using something like params.permit(pictures: [:file]) and then ignoring the indexes in your each block, but leaving Dropzone's source code as is..

How does that sound?

Cheers

ncuesta commented 8 years ago

Oh, I totally missed the configuration part: You should also drop the trailing [] in your paramName setting, like this:

paramName: "pictures",
cielmik commented 8 years ago

hi @ncuesta, it's okay and I appreciate your help.

I'm gonna try your suggestions and report back.

cielmik commented 8 years ago

Here is a sample gallery I made if you want to play around with it

https://github.com/cielmik/dropzone-photoswipe-gallery-rails

This one still has the edited dropzone.js

cielmik commented 8 years ago

@ncuesta I ran into some problem with permitting parameters:

Assuming dropzone.js is not edited (Using the above sample gallery I made)

paramName: "album[images][]", is changed to paramName: "album[images]"

example 3 images again

name=\"album[images][0]\" -> for the 1st image name=\"album[images][1]\" -> for the 2nd image name=\"album[images][2]\" -> for the 3rd image

I am getting Unpermitted parameter: 0,1,2, my album_paramsworks if there is no number inside the []

my params permit

    def album_params
      params.require(:album).permit( :name, :title,  :description, :images => [:images])
    end

BTW, how do you ignore the appended [n] with rails?

it's this params[:album][:pictures].each_with_index |x, y| right?

ncuesta commented 8 years ago

Hi @cielmik , you could just use something like

params.require(:album).permit(:name, :title, :description, pictures: [])

Does that work for you?

cielmik commented 8 years ago

@ncuesta I have finally solved it. Thank you.

This is without editing dropzone.js Here is the answer:

form file get: = file_field_tag "pictures[]", multiple: true

your js for dropzone

     paramName: "pictures",
     uploadMultiple: true

Controller:

 def create
    @album = Album.create(album_params)
    respond_to do |format|
      if @album.save
        save_pictures
        format.html { redirect_to root_path, notice: 'Album was successfully created.' }
        format.json { render json: @album }
      else
        flash[:danger] = "Album has not been saved."
        render 'new'
      end
    end
  end

 private
    def save_pictures
      if params[:pictures]
        params[:pictures].each do |image|
          @album.pictures.create(image: image.pop)
        end
      end
    end

file_field_tag doesnt require any permit

the key is here:

        params[:pictures].each do |image|
          @album.pictures.create(image: image.pop)
        end

each |image| will give two arrays [0, file1], [1, file2], [2, file3], so image.pop will get the files and pass it on to the server.

ncuesta commented 8 years ago

I'm glad you were able to solve it! Cheers.