Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

file upload without activerecord

How do you handle file upload in rail without attaching them to active record ?
I just want to write the files to the disk.

Thanks,

like image 548
Mike Avatar asked Nov 04 '09 19:11

Mike


2 Answers

If I understand correctly what you need then the most simple example would be this:

The controller:

  class UploadController < ApplicationController   def new    end    def create     name = params[:upload][:file].original_filename     path = File.join("public", "images", "upload", name)     File.open(path, "wb") { |f| f.write(params[:upload][:file].read) }     flash[:notice] = "File uploaded"     redirect_to "/upload/new"   end end 

The view:

<% flash.each do |key, msg| %>     <%= content_tag :div, msg, :class => [key, " message"], :id => "notice_#{key}" %> <% end %> <% form_tag '/upload/create', { :multipart => true } do %>     <p>     <%= file_field_tag 'upload[file]' %>     </p>     <p>         <%= submit_tag "Upload" %>     </p> <% end %> 

This would let you upload any file without any checks or validations which in my opinion isn't that usefull.

If I would do it myself then I would use something like validatable gem or tableless gem just tableless is not supported anymore. These gems would allow you to validate what you're uploading to make it more sane.

like image 53
Rytis Lukoševičius Avatar answered Sep 17 '22 18:09

Rytis Lukoševičius


The Tempfile documentation shows an example that's equivalent to Rytis's code, which is fine most of the time. But when you call tempfile.read, Ruby is reading the whole file as a single chunk into memory, which is sub-optimal.

However, FileUtils provides a copy_stream method, and IO, at least in Ruby 2.0, provides a copy_stream implementation that handles writing directly to a filepath (FileUtils.copy_stream requires File-like objects on both sides, or so say the docs).

In my case, I was initiating a large multi-file upload via AJAX, and wanted to avoid reading the whole file(s) into Ruby's memory before writing to disk.

In the example below, params[:files] is an Array of ActionDispatch::Http::UploadedFile instances, and local_filepath is a string pointing to a non-existing file in an existing directory. For brevity, I'll assume I'm only uploading one file:

IO.copy_stream(params[:files][0].tempfile, local_filepath) 

The ActionDispatch::Http::UploadedFile instance has a .tempfile field that's just a regular Tempfile instance.

I'm not actually sure that Ruby still isn't reading the whole file into memory—I didn't benchmark anything—but it's a lot more possible than it is with the localfile.write(tempfile.read) syntax.

tl;dr: IO.copy_stream(your_tempfile, your_disk_filepath) is more concise, if not faster.

like image 33
chbrown Avatar answered Sep 19 '22 18:09

chbrown