views:

3323

answers:

5

I'm looking into the feasibility of adding a function to my Rails-based intranet site that allows users to upload files.

Two purposes: My users are widely distributed geographically and linking to documents on the shared network storage doesn't always work (different addresses, DNS entries and stuff outside my control or interest) so I'm thinking about providing a database-oriented alternative. We have a number of files from which we parse data at the client end. I'd rather like to be able to push that up to the server.

I've looked at attachment_fu, Paperclip and another one (forgotten the name!) all of which seem very image-oriented, although attachment_fu at least can work without a image processing library present, thank goodness.

The big problem is that my server does not permit my application to write files locally, and these plugins all seem to want to create a Tempfile.

The questions (finally!)

Is there a reasonable way to upload binary data and process it in memory and/or store it as a BLOB without any server-side file saves?

Or should I give up on the file distribution idea and give the users a second-best option of copy-and-paste text boxes where possible?

(Closest I could find on SO was this which doesn't really help)

+1  A: 

This HowTo for Rails includes a section (near the end of the page) on how to upload directly to the database. That section is sort of messed up, but the gist of it is that you just read the uploaded file contents into your BLOB field on your ActiveRecord object and save as normal. Since I don't know how you use the file inside your application, I can't really give any advice on how to use it from the database, though there is also a section on downloading from the DB in the HowTo.

It may be easier just to see if you can get permission to write to a single directory, perhaps inside your web app folder, on the server.

tvanfosson
About the permission thing - it's already requested, but our IT people can take an astonishingly long time over stuff like this. Months. Many months. You would not believe how long it took to set up a database instance. Well, maybe you would.
Mike Woodhouse
+1  A: 

You could read the data from the params object, and write it straight to your model.

For example, you could have a form like this.

<% form_for :upload, :url => {:action=>:upload}, :html=>{:multipart=>true} do |f| %>
  <%= f.file_field :file %>
  <%= f.submit 'Upload' %>
<% end %>

Then you can easily get the original filename and the binary data.

class TestController < ApplicationController

  def upload 
    file_param = params[:upload][:file]
    filename = file_param.original_filename
    filedata = file_param.read

    @data = UploadedFile.create(:name => filename, :data => filedata)

    render :text => "created #{@data.id}"
  end

end

Of course your model needs to have the proper columns.

class CreateUploadedFiles < ActiveRecord::Migration
  def self.up
    create_table :uploaded_files do |t|
      t.string :name
      t.binary :data
      t.timestamps
    end
  end

  def self.down
    drop_table :uploaded_files
  end
end

Hope this helps!

blindgaenger
I almost had it - I missed the double-dereferencing on params[:upload][:file]It works, which ought to make things interesting! Thanks.
Mike Woodhouse
A: 

The big problem is that my server does not permit my application to write files locally, and these plugins all seem to want to create a Tempfile.

Yes it does, or you wouldn't be able to upload the files at all.

Rails itself creates tempfiles if the uploaded file is larger than 15k or so.

<%= f.file_field :file %>
....
file_param = params[:upload][:file]

As soon as you upload something bigger than 15k, params[:upload][:file] is going to be an ActionController::UploadedTempFile.

What's the difference? Rails is likely writing it's tempfiles to the global temp directory (which everyone can write to) but the plugins are probably trying to write to RAILS_ROOT/tmp, which your server disallows. The good news is you can just configure those things to use a different temp dir so they can write their tempfiles, and it should all work.

For example, attachment_fu's default temp path is under rails root.. You should be able to change it like this:

Technoweenie::AttachmentFu.tempfile_path = Dir::tmpdir

**PS: pulling the file data straight out of the params and putting it into the database may still be the best way to go. I personally dislike attachment_fu and it's ilk, as they try to do too many things, but either way, it's very useful to know about how the whole uploaded file/temp file thing works in rails :-)

Orion Edwards
Ah. So it does. Of course my test upload file was 13.5K...I wonder what happens on the server (gulp).
Mike Woodhouse
Excellent - I don't know where /tmp is, but Rails is seeming to write a file there without problems, so I don't see that I care.I iz in ur web server messin wit ur spreadsheetz.(something like that)
Mike Woodhouse
A: 

So this code in my controller:

  def upload
    file_content = params[:upload][:file]
    render :text => [
      [:original_path, :content_type, :local_path, :path, :original_filename].collect {|m| file_content.send(m)},
      file_content.class, 
      file_content.size.to_s].flatten.join("<br/>")
  end

gives this for a smaller file:

b_wib.xls
application/vnd.ms-excel


b_wib.xls
ActionController::UploadedStringIO
13824

and this for a larger one:

a_wib.xls
application/vnd.ms-excel
/tmp/CGI.10029.1
/tmp/CGI.10029.1
a_wib.xls
ActionController::UploadedTempfile
27648

...which is exactly as Orion described.

Mike Woodhouse
A: 

For anyone else reading this while just saving the File/IO in params to the database is a nice solution (why complicate matters) Paperclip, and I would suspect attachment_fu, are not image specific. Since uploading images and resizing are very common Paperclip comes bundled with a processor to resize images, but it is not enabled by default and you can easily add your own processors.

Kris