Ruby on Rails
HowtoUploadFiles (Version #1192)

This page is a complete mess! However, it is updated with a working example, compatible with Rails 2.0.2 below.

See also the Rails cookbook: Sending and receiving files

Example of file saving model (based on Rails Cookbook).
This works with Rails 2.0.2, and requires no special modification to controller code. Just make sure your form in the view has :html => { :multipart => true} and that your file field matches the name of the setter in the model code (“cover” in the below example). Also, it uses the ALBUM_COVER_STORAGE_PATH constant, I recommend setting this in a ruby file in the initializers dir.

It is pretty basic, but shows how to write uploaded files to disk using only the model class. This example uses the model id to create a folder for the uploads, and also names the file with the id and the original extension. The reason for this is to make it simple to save several versions of one upload togheter, like sizes, formats etc.

The model


require "ftools"
class Album < ActiveRecord::Base
  belongs_to :profile
  
  validates_presence_of :artist, :title
  
  # run write_file after save to db
  after_save :write_file
  
  # run delete_file method after removal from db
  after_destroy :delete_file
  
  # setter for form file field "cover"
  # grabs the data and sets it to an instance variable.
  # we need this so the model is in db before file save,
  # so we can use the model id as filename.
  def cover=(file_data)
    @file_data = file_data
  end
  
  # write the @file_data data content to disk,
  # using the ALBUM_COVER_STORAGE_PATH constant.
  # saves the file with the filename of the model id
  # together with the file original extension
  def write_file
    if @file_data
      File.makedirs("#{ALBUM_COVER_STORAGE_PATH}/#{id}")
      File.open("#{ALBUM_COVER_STORAGE_PATH}/#{id}/#{id}.#{extension}", "w") { |file| file.write(@file_data.read) }
      # put calls to other logic here - resizing, conversion etc.
    end
  end
  
  # deletes the file(s) by removing the whole dir
  def delete_file
    FileUtils.rm_rf("#{ALBUM_COVER_STORAGE_PATH}/#{id}")
  end
  
  # just gets the extension of uploaded file
  def extension
    @file_data.original_filename.split(".").last
  end
  
end

The controller (just a snippet showing the new and create actions)
As you can se, nothing unusual here, except maybe the current_profile.albums.new(). Change it to Album.new() if needed. <pre><code> def new @album = Album.new end def create @album = @current_profile.albums.new(params[:album]) if @album.save flash[:notice] = "Album created" redirect_to profile_album_path(current_profile, @album)
else
render :action => “new”
end
end

The view (new.html.erb)
Note the use of a partial to render the form fields, very nice to do, as the edit view can use the same partial, and you save typing. :)


<h1>Create new album

<= error_messages_for :album %>
<
form_for [@current_profile, @album], :html => { :multipart => true } do |f| >



    <= render :partial => “fields”, :locals => { :f => f } %>
  •  

  • <%= f.submit “Create” %>

<% end %>

The partial used in the view (_fields.html.erb)


<li>
	<label>Artist name

<%= f.text_field :artist, :value => @current_profile.screen_name, :onclick => (“this.select();” if action?("new")) %>

  • <%= f.text_field :title %>

  • <%= f.text_field :year, :value => Time.now.year, :onclick => (“this.select();” if action?("new")) %>

  • <%= f.file_field :cover %>

  • <%= f.text_area :editorial %>

  • The storage path constant
    This is set in the RAILS_ROOT/config/initializers/globals.rb. Just create the file and name it to something nice, and enter the line in there. This file will load when the server starts/restarts. So restart your server after the update.

    
    # sets the upload root, relative to the RAILS_ROOT
    ALBUM_COVER_STORAGE_PATH = "#{RAILS_ROOT}/../storage/album_covers"
    

    End of example, the following view and controller code does not belong to the above example

    ———

    The form part of the “new” template:

    <form action="create" method="post" enctype="multipart/form-data">
      <p>
        <b>Name:</b><br />
        <%= text_field "person", "name" %>
      </p>
    
      <p>
        <b>Picture:</b><br />
        <input type="file" name="person[picture]" />
      </p>
    
      <p><input type="submit" name="Save" /></p>
    </form>

    or:

    
    <%= form_tag ({:action => "create"}, {:multipart => true}) %>
        <label for="name">Name:</label> 
        <%= text_field "person", "name" %>
        <label for="picture">Picture:</label> 
        <%= file_field_tag "picture" %>
        <%= submit_tag  "Save" %>
    <%= end_form_tag %>
    

    The controller:

    class AddressbookController < ApplicationController
      def new
        # not really needed since the template doesn't rely on any data
      end
    
      def create
        post = Post.save(@params["person"])
        # Doesn't this mean post is a File object?
        # post.id is a bad idea in this case...
    
        redirect_to :action => "show", :id => post.id
      end
    end

    The model:

    class Post < ActiveRecord::Base
      def self.save(person)
        f = File.new("pictures/#{person['name']}/picture.jpg", "wb")
        f.write params[:picture].read
        f.close
      end
    end

    To get the original name of the uploaded file, use person['picture'].original_filename.

    There is also a person['picture'].content_type method. (These are buried somewhere in the depths of cgi.rb) Note: the string returned contenttype() seems to have extra whitespace, so if you are needing to parse or compare it, use strip() on it first._

    PAY ATTENTION Windows Users: to avoid corrupting binary files, you must call File.open in binary mode. Change the “w” flag to “wb”, like this:

    File.open("pictures/#{person['name']}/picture.jpg", "wb") { |f| f.write(person['picture'].read) }

    truncated files?
    If you have trouble with zero-length files written by this code, try calling

    person['picture'].rewind
    before writing the file.

    Files uploaded from Internet Explorer
    Internet Explorer prepends the original path of a file to the filename sent, so the original_filename routine will return something like C:\Documents and Files\user_name\Pictures\My File.jpg instead of just My File.jpg. To deal with this, make sure you write a sanitize method, perhaps called via :before_save, to remove the path and any illegal characters from the filename.

    An example sanitize method (this is not perfect!):

    private
        def sanitize_filename(value)
            # get only the filename, not the whole path
            just_filename = value.gsub(/^.*(\\|\/)/, '')
            # NOTE: File.basename doesn't work right with Windows paths on Unix
            # INCORRECT: just_filename = File.basename(value.gsub('\\\\', '/')) 
            
            # Finally, replace all non alphanumeric, underscore or periods with underscore
            @filename = just_filename.gsub(/[^\w\.\-]/,'_') 
        end

    NOTE: A much better way to handle this is to use the Ruby File library. See File.basename(your_file)

    CGI class documentation

    From the CGI class documentation

    Multipart requests

    If a request’s method is POST and its content type is multipart/form-data, then it may contain uploaded files. These are stored by the \QueryExtension module in the parameters of the request. The parameter name is the name attribute of the file input field, as usual. However, the value is not a string, but an IO object, either an IOString for small files, or a Tempfile for larger ones. This object also has the additional singleton methods:


    local_path():

    the path of the uploaded file on the local filesystem

    original_filename():
    the name of the file on the client computer
    content_type():
    the content type of the file

    Note There is no such thing as an IOString (they meant to say StringIO), and the object returned does not have a local_path method most of the time. The only methods that do work all of the time are: original_filename, content_type, length, and read.

    How to upload files directly into the database

    The view is almost the same as above, but I changed the field’s name to tmp_file since it’s not going to be stored anywhere permanently.

    The model for me is just the plain model without any specific methods at the moment. Oh, in the DB you should have a string field (i.e. ’filename’), and a field ‘picture’ which should be a binary field (for example BLOB in \MySQL).

    The controller:

    def create
      @params['person']['filename'] = @params['person']['tmp_file'].original_filename.gsub(/[^a-zA-Z0-9.]/, '_') # This makes sure filenames are sane
      @params['person']['picture'] = @params['person']['tmp_file'].read
      @params['person'].delete('tmp_file') # let's remove the field from the hash, because there's no such field in the DB anyway.
      @person = Person.new(@params['person'])
      # then the basic if @person.save ... like in <a href="http://wiki.rubyonrails.com/rails/pages/TutorialFramingOut" class="existingWikiWord">TutorialFramingOut</a>
    

    In the above, the file contents gets inserted into the hash as a string – ie. it’s read to memory. Is that the best we can do? Can we not pass a file-reference or similar and via that get the file contents streamed from the filesystem to the DB?

    Note to Postgresql users If you want to use large objects, you can’t just do obj.connection.lo_import() or anything. You have to edit your activerecord library a little… for me, this meant editing this file

    
    /usr/lib/ruby/gems/1.8/gems/activerecord-1.11.1/lib/active_record/connection_adapters/postgresql_adapter.rb
    

    and adding lines like this

    
          def lo_import(file)
            @connection.lo_import(file)
          end
    

    under the PostgreSQLAdapter? class… then you can use those methods on the connection.

    If when trying to upload you get the following


    undefined method `lo_import’ for #

    Do

    gem install postgres

    note: this is for outdated ActiveRecord versions only, the newer variants support:
    self.connection().execute("BEGIN") oid = self.connection().raw_connection.lo_import(file) self.picture = oid.oid() self.connection().execute("COMMIT") self.save

    Its important to put the lo_import into a transaction. In addition to lo_import and lo_export the postgresql(incl. ruby) interface offer things like lo_read, lo_write for direct block access.

    Second Note for Postgresql users : Here is a “solution” that work for me, by using “execute” and SQL statement with lo_import and lo_export. You just have to modify the model describe previously, and create a callback “after_save” in your model. NB, my modelName is not Picture, but Contact, and the field in the postgres db is picture_name, picture_type, and picture_data as an oid. Laurent Buffat @ AltraBio.com .

     def picture=(picture_field)          
          self.picture_name = base_part_of(picture_field.original_filename) 
          self.picture_type = picture_field.content_type.chomp 
          # self.picture_data = picture_field.read doesn't work for postgres
          @temp_file = picture_field.local_path()
          # for some reason that I was not able to understand, local_path sometime, return a empty string
          # maybe, when the path for the orignal_filename is not full accecible 
          # ( the explaination it's not clear, but it's not clear for me what append exactly )
          # So to "correct" the bad behaviorh of "local_path", I use read to make the local_copy
          @temp_file = "/tmp/local_upload_#{self.picture_name}"
          f=File.open(@temp_file,"w")
          f.write(picture_field.read)
          f.close
          @filename = self.picture_name
          @contact_id = self.id
        end
    
     def picture
          @temp_file="/tmp/#{self.picture_name}"
          self.connection().execute "SELECT lo_export(picture_data,'#{@temp_file}') FROM contacts WHERE ID=#{self.id};"
          f = File.open(@temp_file, "rb")
          return f.read
        end
    
     def after_save
            if @temp_file
              FileUtils.chmod 0444, @temp_file
              self.connection().execute "UPDATE contacts SET picture_data = lo_import('#{@temp_file}') WHERE ID=#{@contact_id};"
            end
        end
    

    End of “Second Note”

    How to download files from the database

    Here’s a piece of code that’ll help you download the picture from the DB directly (modified from HowtoSendFiles rev=1)

      @entry = Person.find(@params['id'])
      @response.headers['Pragma'] = ' '
      @response.headers['Cache-Control'] = ' '
      @response.headers['Content-type'] = 'application/octet-stream'
      @response.headers['Content-Disposition'] = "attachment; filename=#{@person.filename}" 
      @response.headers['Accept-Ranges'] = 'bytes'
      @response.headers['Content-Length'] = @person.picture.length
      @response.headers['Content-Transfer-Encoding'] = 'binary'
      @response.headers['Content-Description'] = 'File Transfer'
      render_text @person.picture

    Alternately, you can do the much simpler:

      @person = Person.find(@params['id'])
      send_data @person.picture, :filename => @person.filename, :type => "image/jpeg"
    

    If you want to display the image inline (ie, in a page), use this send_data instead:

      send_data @person.picture, :filename => @person.filename, :type => "image/jpeg", :disposition => "inline"

    Here’s the api documentation for send_file
    and send_data

    [This stuff won’t work in lighttpd due to a bug in lighttpd and file uploads. That was many versions ago, however. It may work now. —what-a-day]
    [But I use lighttpd 1.4.11 and the only problem I ever had was bad permissions on the temp directory. If set wrong, large file uploads wont work. Lighttpd works just fine.

    File save variation

    The uploaded file will be a TempFile -like object (if over 10kb in size). These can be copied by the filesystem instead of being read and processed through Ruby.

      def picture=(picture)
        FileUtils.copy picture.local_path, "pictures/#{name}/picture.jpg"
      end

    In rare cases the file returned can be a String (instead of StringIO or TempFile). (Try it by uploading a thumbs.db file through Safari)

    Using caching to reduce database load and bandwidth usage

    See HowtoUseHTTPCaching

    Checking that the user provided a file:

    This can be done via picture.size == 0

    or like this:
    if picture.kind_of? StringIO or picture.kind_of? Tempfile

    Uploading files to a SQLite database

    You can’t store things with null bytes into BLOBs yet. Until the new adapter is out, use Base64 or some similar binary→ascii coding system to make it not contain null bytes.

    Testing a file upload

    Use fixture_file_upload to simulate an uploaded file.

    Other approaches

    • There is another example at
      http://www.albert.bagasie.com/RailsTips
    • Sebastian Kanthak has a useful file_column extension that makes handling file uploads really easy.
    • TinyFile — a quick, ready-to-go example of how to do some basic file uploads in a real rails app
    • acts_as_attachment offers very similar functionality to file_column. It provides options for image thumbnailing, and can store files in the database or filesystem.
    • attachment_fu is the new version of the acts_as_attachment by Rick Olson. New features include the ability to upload to Amazon S3 and more… Check out this tutorial

    Mime-types

    No mention of mime-types here. You could try the MIME::Types library for Ruby

    Germany!

    “Screencast on How to upload images”: http://www.rubyplus.org/episodes/31-How-to-upload-images-in-Rails-2-.html

    category:Howto

    This page is a complete mess! However, it is updated with a working example, compatible with Rails 2.0.2 below.

    See also the Rails cookbook: Sending and receiving files

    Example of file saving model (based on Rails Cookbook).
    This works with Rails 2.0.2, and requires no special modification to controller code. Just make sure your form in the view has :html => { :multipart => true} and that your file field matches the name of the setter in the model code (“cover” in the below example). Also, it uses the ALBUM_COVER_STORAGE_PATH constant, I recommend setting this in a ruby file in the initializers dir.

    It is pretty basic, but shows how to write uploaded files to disk using only the model class. This example uses the model id to create a folder for the uploads, and also names the file with the id and the original extension. The reason for this is to make it simple to save several versions of one upload togheter, like sizes, formats etc.

    The model

    
    require "ftools"
    class Album < ActiveRecord::Base
      belongs_to :profile
      
      validates_presence_of :artist, :title
      
      # run write_file after save to db
      after_save :write_file
      
      # run delete_file method after removal from db
      after_destroy :delete_file
      
      # setter for form file field "cover"
      # grabs the data and sets it to an instance variable.
      # we need this so the model is in db before file save,
      # so we can use the model id as filename.
      def cover=(file_data)
        @file_data = file_data
      end
      
      # write the @file_data data content to disk,
      # using the ALBUM_COVER_STORAGE_PATH constant.
      # saves the file with the filename of the model id
      # together with the file original extension
      def write_file
        if @file_data
          File.makedirs("#{ALBUM_COVER_STORAGE_PATH}/#{id}")
          File.open("#{ALBUM_COVER_STORAGE_PATH}/#{id}/#{id}.#{extension}", "w") { |file| file.write(@file_data.read) }
          # put calls to other logic here - resizing, conversion etc.
        end
      end
      
      # deletes the file(s) by removing the whole dir
      def delete_file
        FileUtils.rm_rf("#{ALBUM_COVER_STORAGE_PATH}/#{id}")
      end
      
      # just gets the extension of uploaded file
      def extension
        @file_data.original_filename.split(".").last
      end
      
    end
    

    The controller (just a snippet showing the new and create actions)
    As you can se, nothing unusual here, except maybe the current_profile.albums.new(). Change it to Album.new() if needed. <pre><code> def new @album = Album.new end def create @album = @current_profile.albums.new(params[:album]) if @album.save flash[:notice] = "Album created" redirect_to profile_album_path(current_profile, @album)
    else
    render :action => “new”
    end
    end

    The view (new.html.erb)
    Note the use of a partial to render the form fields, very nice to do, as the edit view can use the same partial, and you save typing. :)

    
    <h1>Create new album

    <= error_messages_for :album %>
    <
    form_for [@current_profile, @album], :html => { :multipart => true } do |f| >



      <= render :partial => “fields”, :locals => { :f => f } %>
    •  

    • <%= f.submit “Create” %>

    <% end %>

    The partial used in the view (_fields.html.erb)

    
    <li>
    	<label>Artist name

    <%= f.text_field :artist, :value => @current_profile.screen_name, :onclick => (“this.select();” if action?("new")) %>

  • <%= f.text_field :title %>

  • <%= f.text_field :year, :value => Time.now.year, :onclick => (“this.select();” if action?("new")) %>

  • <%= f.file_field :cover %>

  • <%= f.text_area :editorial %>

  • The storage path constant
    This is set in the RAILS_ROOT/config/initializers/globals.rb. Just create the file and name it to something nice, and enter the line in there. This file will load when the server starts/restarts. So restart your server after the update.

    
    # sets the upload root, relative to the RAILS_ROOT
    ALBUM_COVER_STORAGE_PATH = "#{RAILS_ROOT}/../storage/album_covers"
    

    End of example, the following view and controller code does not belong to the above example

    ———

    The form part of the “new” template:

    <form action="create" method="post" enctype="multipart/form-data">
      <p>
        <b>Name:</b><br />
        <%= text_field "person", "name" %>
      </p>
    
      <p>
        <b>Picture:</b><br />
        <input type="file" name="person[picture]" />
      </p>
    
      <p><input type="submit" name="Save" /></p>
    </form>

    or:

    
    <%= form_tag ({:action => "create"}, {:multipart => true}) %>
        <label for="name">Name:</label> 
        <%= text_field "person", "name" %>
        <label for="picture">Picture:</label> 
        <%= file_field_tag "picture" %>
        <%= submit_tag  "Save" %>
    <%= end_form_tag %>
    

    The controller:

    class AddressbookController < ApplicationController
      def new
        # not really needed since the template doesn't rely on any data
      end
    
      def create
        post = Post.save(@params["person"])
        # Doesn't this mean post is a File object?
        # post.id is a bad idea in this case...
    
        redirect_to :action => "show", :id => post.id
      end
    end

    The model:

    class Post < ActiveRecord::Base
      def self.save(person)
        f = File.new("pictures/#{person['name']}/picture.jpg", "wb")
        f.write params[:picture].read
        f.close
      end
    end

    To get the original name of the uploaded file, use person['picture'].original_filename.

    There is also a person['picture'].content_type method. (These are buried somewhere in the depths of cgi.rb) Note: the string returned contenttype() seems to have extra whitespace, so if you are needing to parse or compare it, use strip() on it first._

    PAY ATTENTION Windows Users: to avoid corrupting binary files, you must call File.open in binary mode. Change the “w” flag to “wb”, like this:

    File.open("pictures/#{person['name']}/picture.jpg", "wb") { |f| f.write(person['picture'].read) }

    truncated files?
    If you have trouble with zero-length files written by this code, try calling

    person['picture'].rewind
    before writing the file.

    Files uploaded from Internet Explorer
    Internet Explorer prepends the original path of a file to the filename sent, so the original_filename routine will return something like C:\Documents and Files\user_name\Pictures\My File.jpg instead of just My File.jpg. To deal with this, make sure you write a sanitize method, perhaps called via :before_save, to remove the path and any illegal characters from the filename.

    An example sanitize method (this is not perfect!):

    private
        def sanitize_filename(value)
            # get only the filename, not the whole path
            just_filename = value.gsub(/^.*(\\|\/)/, '')
            # NOTE: File.basename doesn't work right with Windows paths on Unix
            # INCORRECT: just_filename = File.basename(value.gsub('\\\\', '/')) 
            
            # Finally, replace all non alphanumeric, underscore or periods with underscore
            @filename = just_filename.gsub(/[^\w\.\-]/,'_') 
        end

    NOTE: A much better way to handle this is to use the Ruby File library. See File.basename(your_file)

    CGI class documentation

    From the CGI class documentation

    Multipart requests

    If a request’s method is POST and its content type is multipart/form-data, then it may contain uploaded files. These are stored by the \QueryExtension module in the parameters of the request. The parameter name is the name attribute of the file input field, as usual. However, the value is not a string, but an IO object, either an IOString for small files, or a Tempfile for larger ones. This object also has the additional singleton methods:


    local_path():

    the path of the uploaded file on the local filesystem

    original_filename():
    the name of the file on the client computer
    content_type():
    the content type of the file

    Note There is no such thing as an IOString (they meant to say StringIO), and the object returned does not have a local_path method most of the time. The only methods that do work all of the time are: original_filename, content_type, length, and read.

    How to upload files directly into the database

    The view is almost the same as above, but I changed the field’s name to tmp_file since it’s not going to be stored anywhere permanently.

    The model for me is just the plain model without any specific methods at the moment. Oh, in the DB you should have a string field (i.e. ’filename’), and a field ‘picture’ which should be a binary field (for example BLOB in \MySQL).

    The controller:

    def create
      @params['person']['filename'] = @params['person']['tmp_file'].original_filename.gsub(/[^a-zA-Z0-9.]/, '_') # This makes sure filenames are sane
      @params['person']['picture'] = @params['person']['tmp_file'].read
      @params['person'].delete('tmp_file') # let's remove the field from the hash, because there's no such field in the DB anyway.
      @person = Person.new(@params['person'])
      # then the basic if @person.save ... like in <a href="http://wiki.rubyonrails.com/rails/pages/TutorialFramingOut" class="existingWikiWord">TutorialFramingOut</a>
    

    In the above, the file contents gets inserted into the hash as a string – ie. it’s read to memory. Is that the best we can do? Can we not pass a file-reference or similar and via that get the file contents streamed from the filesystem to the DB?

    Note to Postgresql users If you want to use large objects, you can’t just do obj.connection.lo_import() or anything. You have to edit your activerecord library a little… for me, this meant editing this file

    
    /usr/lib/ruby/gems/1.8/gems/activerecord-1.11.1/lib/active_record/connection_adapters/postgresql_adapter.rb
    

    and adding lines like this

    
          def lo_import(file)
            @connection.lo_import(file)
          end
    

    under the PostgreSQLAdapter? class… then you can use those methods on the connection.

    If when trying to upload you get the following


    undefined method `lo_import’ for #

    Do

    gem install postgres

    note: this is for outdated ActiveRecord versions only, the newer variants support:
    self.connection().execute("BEGIN") oid = self.connection().raw_connection.lo_import(file) self.picture = oid.oid() self.connection().execute("COMMIT") self.save

    Its important to put the lo_import into a transaction. In addition to lo_import and lo_export the postgresql(incl. ruby) interface offer things like lo_read, lo_write for direct block access.

    Second Note for Postgresql users : Here is a “solution” that work for me, by using “execute” and SQL statement with lo_import and lo_export. You just have to modify the model describe previously, and create a callback “after_save” in your model. NB, my modelName is not Picture, but Contact, and the field in the postgres db is picture_name, picture_type, and picture_data as an oid. Laurent Buffat @ AltraBio.com .

     def picture=(picture_field)          
          self.picture_name = base_part_of(picture_field.original_filename) 
          self.picture_type = picture_field.content_type.chomp 
          # self.picture_data = picture_field.read doesn't work for postgres
          @temp_file = picture_field.local_path()
          # for some reason that I was not able to understand, local_path sometime, return a empty string
          # maybe, when the path for the orignal_filename is not full accecible 
          # ( the explaination it's not clear, but it's not clear for me what append exactly )
          # So to "correct" the bad behaviorh of "local_path", I use read to make the local_copy
          @temp_file = "/tmp/local_upload_#{self.picture_name}"
          f=File.open(@temp_file,"w")
          f.write(picture_field.read)
          f.close
          @filename = self.picture_name
          @contact_id = self.id
        end
    
     def picture
          @temp_file="/tmp/#{self.picture_name}"
          self.connection().execute "SELECT lo_export(picture_data,'#{@temp_file}') FROM contacts WHERE ID=#{self.id};"
          f = File.open(@temp_file, "rb")
          return f.read
        end
    
     def after_save
            if @temp_file
              FileUtils.chmod 0444, @temp_file
              self.connection().execute "UPDATE contacts SET picture_data = lo_import('#{@temp_file}') WHERE ID=#{@contact_id};"
            end
        end
    

    End of “Second Note”

    How to download files from the database

    Here’s a piece of code that’ll help you download the picture from the DB directly (modified from HowtoSendFiles rev=1)

      @entry = Person.find(@params['id'])
      @response.headers['Pragma'] = ' '
      @response.headers['Cache-Control'] = ' '
      @response.headers['Content-type'] = 'application/octet-stream'
      @response.headers['Content-Disposition'] = "attachment; filename=#{@person.filename}" 
      @response.headers['Accept-Ranges'] = 'bytes'
      @response.headers['Content-Length'] = @person.picture.length
      @response.headers['Content-Transfer-Encoding'] = 'binary'
      @response.headers['Content-Description'] = 'File Transfer'
      render_text @person.picture

    Alternately, you can do the much simpler:

      @person = Person.find(@params['id'])
      send_data @person.picture, :filename => @person.filename, :type => "image/jpeg"
    

    If you want to display the image inline (ie, in a page), use this send_data instead:

      send_data @person.picture, :filename => @person.filename, :type => "image/jpeg", :disposition => "inline"

    Here’s the api documentation for send_file
    and send_data

    [This stuff won’t work in lighttpd due to a bug in lighttpd and file uploads. That was many versions ago, however. It may work now. —what-a-day]
    [But I use lighttpd 1.4.11 and the only problem I ever had was bad permissions on the temp directory. If set wrong, large file uploads wont work. Lighttpd works just fine.

    File save variation

    The uploaded file will be a TempFile -like object (if over 10kb in size). These can be copied by the filesystem instead of being read and processed through Ruby.

      def picture=(picture)
        FileUtils.copy picture.local_path, "pictures/#{name}/picture.jpg"
      end

    In rare cases the file returned can be a String (instead of StringIO or TempFile). (Try it by uploading a thumbs.db file through Safari)

    Using caching to reduce database load and bandwidth usage

    See HowtoUseHTTPCaching

    Checking that the user provided a file:

    This can be done via picture.size == 0

    or like this:
    if picture.kind_of? StringIO or picture.kind_of? Tempfile

    Uploading files to a SQLite database

    You can’t store things with null bytes into BLOBs yet. Until the new adapter is out, use Base64 or some similar binary→ascii coding system to make it not contain null bytes.

    Testing a file upload

    Use fixture_file_upload to simulate an uploaded file.

    Other approaches

    • There is another example at
      http://www.albert.bagasie.com/RailsTips
    • Sebastian Kanthak has a useful file_column extension that makes handling file uploads really easy.
    • TinyFile — a quick, ready-to-go example of how to do some basic file uploads in a real rails app
    • acts_as_attachment offers very similar functionality to file_column. It provides options for image thumbnailing, and can store files in the database or filesystem.
    • attachment_fu is the new version of the acts_as_attachment by Rick Olson. New features include the ability to upload to Amazon S3 and more… Check out this tutorial

    Mime-types

    No mention of mime-types here. You could try the MIME::Types library for Ruby

    Germany!

    “Screencast on How to upload images”: http://www.rubyplus.org/episodes/31-How-to-upload-images-in-Rails-2-.html

    category:Howto