Uploading Files to Rails Applications

Your application may allow users to upload files. For example, a bug-reporting system might let users attach log files and code samples to a problem ticket, or a blogging application could let its users upload a small image to appear next to their articles.

In HTTP, files are uploaded as a multipart/form-data POST message. As the name suggests, forms are used to generate this type of message. Within that form, you’ll use <input> tags with type="file". When rendered by a browser, this allows the user to select a file by name. When the form is subsequently submitted, the file or files will be sent back along with the rest of the form data.

To illustrate the file upload process, we’ll show some code that allows a user to upload an image and display that image alongside a comment. To do this, we first need a pictures table to store the data:

 class​ CreatePictures < ActiveRecord::Migration
 def​ ​change
  create_table ​:pictures​ ​do​ |t|
  t.​string​ ​:comment
  t.​string​ ​:name
  t.​string​ ​:content_type
 # If using MySQL, blobs default to 64k, so we have to give
 # an explicit size to extend them
  t.​binary​ ​:data​, ​:limit​ => 1.​megabyte
 end
 end
 end

We’ll create a somewhat artificial upload controller just to demonstrate the process. The get action is pretty conventional; it simply creates a new picture object and renders a form:

 class​ UploadController < ApplicationController
 def​ ​get
  @picture = Picture.​new
 end
 # . . .
 private
 # Never trust parameters from the scary internet, only allow the white
 # list through.
 def​ ​picture_params
  params.​require​(​:picture​).​permit​(​:comment​, ​:uploaded_picture​)
 end
 end

The get template contains the form that uploads the picture (along with a comment). Note how we override the encoding type to allow data to be sent back with the response:

 <%=​ form_for(​:picture​,
 url: ​{​action: ​​'save'​},
 html: ​{​multipart: ​​true​}) ​do​ |form| ​%>
 
  Comment: ​<%=​ form.​text_field​(​"comment"​) ​%>​<br/>
  Upload your picture: ​<%=​ form.​file_field​(​"uploaded_picture"​) ​%>​<br/>
 
 <%=​ submit_tag(​"Upload file"​) ​%>
 <%​ ​end​ ​%>

The form has one other subtlety. The picture uploads into an attribute called uploaded_picture. However, the database table doesn’t contain a column of that name. That means that there must be some magic happening in the model:

 class​ Picture < ActiveRecord::Base
 
  validates_format_of ​:content_type​,
 with: ​​/Aimage/​,
 message: ​​"must be a picture"
 
 def​ ​uploaded_picture​=(picture_field)
  self.​name​ = base_part_of(picture_field.​original_filename​)
  self.​content_type​ = picture_field.​content_type​.​chomp
  self.​data​ = picture_field.​read
 end
 
 def​ ​base_part_of​(file_name)
  File.​basename​(file_name).​gsub​(​/[^w._-]/​, ​''​)
 end
 end

We define an accessor called uploaded_picture= to receive the file uploaded by the form. The object returned by the form is an interesting hybrid. It is file-like, so we can read its contents with the read method; that’s how we get the image data into the data column. It also has the attributes content_type and original_filename, which let us get at the uploaded file’s metadata. Accessor methods pick all this apart, resulting in a single object stored as separate attributes in the database.

Note that we also add a validation to check that the content type is of the form image/xxx. We don’t want someone uploading JavaScript.

The save action in the controller is totally conventional:

 def​ ​save
  @picture = Picture.​new​(picture_params)
 if​ @picture.​save
  redirect_to(​action: ​​'show'​, ​id: ​@picture.​id​)
 else
  render(​action: :get​)
 end
 end

Now that we have an image in the database, how do we display it? One way is to give it its own URL and link to that URL from an image tag. For example, we could use a URL such as upload/picture/123 to return the image for picture 123. This would use send_data to return the image to the browser. Note how we set the content type and filename—this lets browsers interpret the data and supplies a default name should the user choose to save the image:

 def​ ​picture
  @picture = Picture.​find​(params[​:id​])
  send_data(@picture.​data​,
 filename: ​@picture.​name​,
 type: ​@picture.​content_type​,
 disposition: ​​"inline"​)
 end

Finally, we can implement the show action, which displays the comment and the image. The action simply loads the picture model object:

 def​ ​show
  @picture = Picture.​find​(params[​:id​])
 end

In the template, the image tag links back to the action that returns the picture content. In the following screenshot, we can see the get and show actions.

images/file_upload.png
 <h3>​<%=​ @picture.​comment​ ​%>​</h3>
 
 <img src=​"​​<%=​ url_for(​:action​ => ​'picture'​, ​:id​ => @picture.​id​) ​%>​​"​/>

If you’d like an easier way of dealing with uploading and storing images, take a look at thoughtbot’s Paperclip[106] or Rick Olson’s attachment_fu[107] plugins. Create a database table that includes a given set of columns (documented on Rick’s site), and the plugin will automatically manage storing both the uploaded data and the upload’s metadata. Unlike our previous approach, it handles storing the uploads in either your filesystem or a database table.

Forms and uploads are just two examples of helpers that Rails provides. Next we’ll show you how you can provide your own helpers and introduce you to a number of other helpers that come with Rails.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.119.131.178