When talking about uploading files in a web application, the way most of us think of is to use a form with an <input type="file">
tag:
1 2 3 4 5 | <form action="/action_page.php"> <input type="file" name="pic" accept="image/*"> <input type="submit"> </form> |
The above method creates a rather tedious file upload experience like this:
Compared to the above method, Dropzone.js library provides users with a much more friendly file upload experience like this:
This library provides a very clear and easy to configure system from the view template, the upload file, and the file validate, … You can learn how to use it here . In this article, I will guide you to use dropzone.js and carrierwave to create a media gallery (which stores video, audio and image files) based on framework rails.
Create rails app
I will create a simple rails app with the following information:
- There is a Media panel to save uploaded file information.
- There is a page to upload the file to.
- There is a page to display all uploaded files.
First, create a basic rails app with the following 2 gems installed
1 2 3 4 | <span class="token comment">#Gemfile</span> gem <span class="token string">'carrierwave'</span> <span class="token punctuation">,</span> <span class="token string">'~> 2.0'</span> gem <span class="token string">'dropzonejs-rails'</span> |
Create Media model and a FileUploader
1 2 3 4 5 6 7 8 | <span class="token keyword">class</span> <span class="token class-name">ImageUploader</span> <span class="token operator"><</span> <span class="token constant">CarrierWave</span> <span class="token punctuation">:</span> <span class="token punctuation">:</span> <span class="token constant">Uploader</span> <span class="token punctuation">:</span> <span class="token punctuation">:</span> <span class="token constant">Base</span> storage <span class="token symbol">:file</span> <span class="token keyword">def</span> store_dir <span class="token string">"uploads/ <span class="token interpolation"><span class="token delimiter tag">#{</span> model <span class="token punctuation">.</span> <span class="token keyword">class</span> <span class="token punctuation">.</span> to_s <span class="token punctuation">.</span> underscore <span class="token delimiter tag">}</span></span> / <span class="token interpolation"><span class="token delimiter tag">#{</span> mounted_as <span class="token delimiter tag">}</span></span> / <span class="token interpolation"><span class="token delimiter tag">#{</span> model <span class="token punctuation">.</span> id <span class="token delimiter tag">}</span></span> "</span> <span class="token keyword">end</span> <span class="token keyword">end</span> |
1 2 3 4 5 6 7 8 9 | class CreateSessions < ActiveRecord::Migration[5.1] def change create_table :medias do |t| t.string :data t.timestamps end end end |
1 2 3 4 | <span class="token keyword">class</span> <span class="token class-name">Media</span> <span class="token operator"><</span> <span class="token constant">ApplicationRecord</span> mount_uploader <span class="token symbol">:data</span> <span class="token punctuation">,</span> <span class="token constant">FileUploader</span> <span class="token keyword">end</span> |
Now I will start creating the file upload page. Create the path for that page first in the routes.rb
file:
1 2 | resoure :medias, only: [:index, :new] |
Create controller with the corresponding action new
for the file upload page:
1 2 3 4 5 | <span class="token keyword">class</span> <span class="token class-name">MediasController</span> <span class="token operator"><</span> <span class="token constant">ApplicationController</span> <span class="token keyword">def</span> <span class="token keyword">new</span> <span class="token class-name">end</span> <span class="token keyword">end</span> |
Create a view for the file download page: new.html.erb
1 2 3 4 5 | <span class="token tag"><span class="token tag"><span class="token punctuation"><</span> div</span> <span class="token attr-name">id</span> <span class="token attr-value"><span class="token punctuation">=</span> <span class="token punctuation">"</span> mydropzone <span class="token punctuation">"</span></span> <span class="token attr-name">class</span> <span class="token attr-value"><span class="token punctuation">=</span> <span class="token punctuation">"</span> dropzone col-md-8 <span class="token punctuation">"</span></span> <span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span> p</span> <span class="token attr-name">class</span> <span class="token attr-value"><span class="token punctuation">=</span> <span class="token punctuation">"</span> row text-center <span class="token punctuation">"</span></span> <span class="token punctuation">></span></span> Kéo, thả file vào đây hoặc <span class="token tag"><span class="token tag"><span class="token punctuation"></</span> p</span> <span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span> div</span> <span class="token attr-name">class</span> <span class="token attr-value"><span class="token punctuation">=</span> <span class="token punctuation">"</span> text-center <span class="token punctuation">"</span></span> <span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span> button</span> <span class="token attr-name">type</span> <span class="token attr-value"><span class="token punctuation">=</span> <span class="token punctuation">"</span> button <span class="token punctuation">"</span></span> <span class="token attr-name">id</span> <span class="token attr-value"><span class="token punctuation">=</span> <span class="token punctuation">"</span> upload_button <span class="token punctuation">"</span></span> <span class="token attr-name">class</span> <span class="token attr-value"><span class="token punctuation">=</span> <span class="token punctuation">"</span> row <span class="token punctuation">"</span></span> <span class="token punctuation">></span></span> chọn nút này để tải file lên <span class="token tag"><span class="token tag"><span class="token punctuation"></</span> button</span> <span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span> div</span> <span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span> div</span> <span class="token punctuation">></span></span> |
And we will get a view like this.
Now we will handle the file upload through the dropzone library functions in a javascript file, I named it upload.js
. First, add file upload.js
to asset pipelines:
1 2 | //= require upload |
Create app/assets/javascripts/upload.js
1 2 3 4 5 6 | <span class="token keyword">var</span> dropzone <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Dropzone</span> <span class="token punctuation">(</span> <span class="token string">"#mydropzone"</span> <span class="token punctuation">,</span> <span class="token punctuation">{</span> dictDefaultMessage <span class="token punctuation">:</span> <span class="token string">""</span> <span class="token punctuation">,</span> uploadMultiple <span class="token punctuation">:</span> <span class="token boolean">true</span> <span class="token punctuation">,</span> url <span class="token punctuation">:</span> <span class="token string">"/medias"</span> <span class="token punctuation">,</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> |
In the above js file, we initialize a Dropzone object with the properties:
dictDefaultMessage
: A message displayed in elements with a class of .dropzone. Here I left blank to customize the view.uploadMultiple: true
. Allows you to choose to upload multiple files.url:
is the path to make the POST request after the file uploads. Currently when clicking ondiv#mydropzone
there will be a file upload window like this: But I want only when clicking on thebutton
chọn nút này để tải file lên
will the file selection window pop up, so I will customize one moreclickable
attribute:
1 2 3 4 5 6 7 | <span class="token keyword">var</span> dropzone <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Dropzone</span> <span class="token punctuation">(</span> <span class="token string">"#mydropzone"</span> <span class="token punctuation">,</span> <span class="token punctuation">{</span> dictDefaultMessage <span class="token punctuation">:</span> <span class="token string">""</span> <span class="token punctuation">,</span> uploadMultiple <span class="token punctuation">:</span> <span class="token boolean">true</span> <span class="token punctuation">,</span> clickable <span class="token punctuation">:</span> <span class="token string">"#upload_button"</span> <span class="token punctuation">,</span> url <span class="token punctuation">:</span> <span class="token string">"/medias"</span> <span class="token punctuation">,</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> |
Now it seems to work as it should:
Now, by default, when you select a file in the popup window or drag / drop the file into the div#dropzone
like this, a POST /medias
request will be made: And the data that it transmits to the server in params will look like this:
1 2 | Parameters: {"file"=>{"0"=>#<ActionDispatch::Http::UploadedFile:0x00007f04db706b10 @tempfile=#<Tempfile:/tmp/RackMultipart20191120-8117-eh06il.png>, @original_filename="2.png", @content_type="image/png", @headers="Content-Disposition: form-data; name="file[0]"; filename="2.png"rnContent-Type: image/pngrn">, "1"=>#<ActionDispatch::Http::UploadedFile:0x00007f04db7068e0 @tempfile=#<Tempfile:/tmp/RackMultipart20191120-8117-1qp6iy5.png>, @original_filename="3.png", @content_type="image/png", @headers="Content-Disposition: form-data; name="file[1]"; filename="3.png"rnContent-Type: image/pngrn">, "2"=>#<ActionDispatch::Http::UploadedFile:0x00007f04db7067c8 @tempfile=#<Tempfile:/tmp/RackMultipart20191120-8117-pgbwmo.png>, @original_filename="4.png", @content_type="image/png", @headers="Content-Disposition: form-data; name="file[2]"; filename="4.png"rnContent-Type: image/pngrn">, "3"=>#<ActionDispatch::Http::UploadedFile:0x00007f04db706700 @tempfile=#<Tempfile:/tmp/RackMultipart20191120-8117-1d6xdsn.png>, @original_filename="5.png", @content_type="image/png", @headers="Content-Disposition: form-data; name="file[3]"; filename="5.png"rnContent-Type: image/pngrn">}} |
Now I will handle the server side to be able to save this data into the db
1 2 3 4 5 6 7 8 9 10 11 | <span class="token keyword">class</span> <span class="token class-name">MediasController</span> <span class="token operator"><</span> <span class="token constant">ApplicationController</span> <span class="token keyword">def</span> <span class="token keyword">new</span> <span class="token class-name">end</span> <span class="token keyword">def</span> create params <span class="token punctuation">[</span> <span class="token symbol">:file</span> <span class="token punctuation">]</span> <span class="token punctuation">.</span> <span class="token keyword">each</span> <span class="token keyword">do</span> <span class="token operator">|</span> key <span class="token punctuation">,</span> file <span class="token operator">|</span> <span class="token constant">Media</span> <span class="token punctuation">.</span> create data <span class="token punctuation">:</span> params <span class="token punctuation">[</span> <span class="token symbol">:file</span> <span class="token punctuation">]</span> <span class="token keyword">end</span> <span class="token keyword">end</span> <span class="token keyword">end</span> |
So the data, after being uploaded, will be saved directly to the database. Now I want to handle, so that after uploading all the files successfully, the redirect to the page showing a list of all images. First, we handle the successmultiple
event for the dropzone
variable as follows:
1 2 3 4 5 6 7 8 9 | var dropzone = new Dropzone ("#mydropzone", { dictDefaultMessage: "", uploadMultiple: true, url: "/medias", successmultiple: { window.location.href = '/medias'; } }); |
With the above processing, after all the files have been uploaded successfully, the browser will automatically redirect to the list /medias
display page. We make the page display the list:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <span class="token keyword">class</span> <span class="token class-name">MediasController</span> <span class="token operator"><</span> <span class="token constant">ApplicationController</span> <span class="token keyword">def</span> <span class="token keyword">new</span> <span class="token class-name">end</span> <span class="token keyword">def</span> index <span class="token variable">@medias</span> <span class="token operator">=</span> <span class="token constant">Media</span> <span class="token punctuation">.</span> all <span class="token keyword">end</span> <span class="token keyword">def</span> create params <span class="token punctuation">[</span> <span class="token symbol">:file</span> <span class="token punctuation">]</span> <span class="token punctuation">.</span> <span class="token keyword">each</span> <span class="token keyword">do</span> <span class="token operator">|</span> key <span class="token punctuation">,</span> file <span class="token operator">|</span> <span class="token constant">Media</span> <span class="token punctuation">.</span> create data <span class="token punctuation">:</span> params <span class="token punctuation">[</span> <span class="token symbol">:file</span> <span class="token punctuation">]</span> <span class="token keyword">end</span> <span class="token keyword">end</span> <span class="token keyword">end</span> |
1 2 3 4 5 | <span class="token comment">#medias/index.html.erb</span> <span class="token operator"><</span> <span class="token operator">%</span> <span class="token variable">@medias</span> <span class="token punctuation">.</span> <span class="token keyword">each</span> <span class="token keyword">do</span> <span class="token operator">|</span> media <span class="token operator">|</span> <span class="token string">%> <%= image_tag media.url, size: "100x100"%></span> <span class="token operator"><</span> <span class="token operator">%</span> <span class="token keyword">end</span> <span class="token operator">%</span> <span class="token operator">></span> |
And see the handling section:
Validate at client_size with dropzone.js
Here, I just want to upload files with the format ".jpg, .png, .mp4, .mp3"
and files smaller than 10Mb, then I can add two attributes to the dropzone variables acceptedFiles
and maxFilesize
1 2 3 4 5 6 7 8 9 10 11 | <span class="token keyword">var</span> dropzone <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Dropzone</span> <span class="token punctuation">(</span> <span class="token string">"#mydropzone"</span> <span class="token punctuation">,</span> <span class="token punctuation">{</span> dictDefaultMessage <span class="token punctuation">:</span> <span class="token string">""</span> <span class="token punctuation">,</span> uploadMultiple <span class="token punctuation">:</span> <span class="token boolean">true</span> <span class="token punctuation">,</span> url <span class="token punctuation">:</span> <span class="token string">"/medias"</span> <span class="token punctuation">,</span> acceptedFiles <span class="token punctuation">:</span> <span class="token string">".jpg, .png, .mp4, .mp3"</span> <span class="token punctuation">,</span> maxFilesize <span class="token punctuation">:</span> <span class="token number">10</span> <span class="token punctuation">,</span> successmultiple <span class="token punctuation">:</span> <span class="token punctuation">{</span> window <span class="token punctuation">.</span> location <span class="token punctuation">.</span> href <span class="token operator">=</span> <span class="token string">'/medias'</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> |
And the error message will be displayed as follows:
In addition, there are many other attributes and events that you can customize here . My post pauses here.
Reference: https://www.dropzonejs.com/