fivetwentysix
fivetwentysix

Reputation: 7487

Rails sends 0 byte files using send_file

I can't get send_file(Model.attachment.path) to work. It doesn't fail, instead, it sends a 0 byte size file to the client, the file names are correct though.

This problem started happening after I did a big migration from Rails 2.3.8 to 3.

There were a lot of other things that took place in this migration and I will try my best to detail all of them.

  1. Distrubution change/Server Change. Rackspace RHEL5 to Linode Ubuntu 10.04LTS
  2. Ruby version change, 1.8.6 -> 1.9.2
  3. Rails version change, 2.3.8 -> 3.0.0
  4. httpd platform change, apache2 -> nginx (However I tried on apache2 as well and it did not work).

I moved the attachments via ftp as they were not part of my git repositories so they were published via cap deploy, instead manual ftp remote(RHEL5) to local(Win7) then local(Win7) to remote(Ubuntu10).

I do know that FTPing does not retain the file permissions through the transfers, so what I've also done is mimicked the chmods that were seen on my previous servers so they are almost identical. (users/groups are different, set to root:root instead of olduser:olduser).

A snippet of the request to download a attachment from my production log.

Started GET "/attachments/replies/1410?1277105698" for 218.102.140.205 at 2010-09-16 09:44:31 +0000
  Processing by AttachmentsController#replies as HTML
  Parameters: {"1277105698"=>nil, "id"=>"1410"}
Sent file /srv/app/releases/20100916094249/attachments/replies/UE0003-Requisition_For_Compensation_Leave.doc (0.2ms)
Completed 200 OK in 78ms

Everything's okay. Let me also rule out local issues, I've tried downloading via Chrome on both Win7 and Ubuntu (on Vbox).

Let me also assure you that the path is indeed correct.

root@li162-41:/srv/app/current# tail /srv/app/releases/20100916094249/attachments/replies/UE0003-Requisition_For_Compensation_Leave.doc
#
    #
         %17nw
                 HQ��+1ae����
                                             %33333333333(��QR���HX�"%%��@9
��@�p4��#P@��Unknown������������G��z �Times New Roman5��Symbol3&�
                       �z �Arial5&�

So to sum up the question, how do I get send_file to actually send files instead of fake 0 byte junk.

Upvotes: 23

Views: 12979

Answers (5)

James Tan
James Tan

Reputation: 1366

On Rails 4, I realize my problem is that I deleted the temporary file which I've generated to send to user.

If I didn't delete the file, send_file works. I've not tested on thin but it works great on Passenger 5 as stand-alone server.

Upvotes: 1

NoICE
NoICE

Reputation: 141

Yes, I had the same problem with X-sendfile being enabled by default in Rails 3 too.

If you have large volume of "send_file" calls, you can just comment-out following line in config/environments/production.rb:

#config.action_dispatch.x_sendfile_header = "X-Sendfile"

Then send_file method started working perfectly.

Because I can't install x-sendfile extension to Apache, I just searched a little and found this.

I hope it helps.

Upvotes: 14

gertas
gertas

Reputation: 17145

send_file has :x_sendfile param which defaults to true in Rails 3. This feature offloads streaming download to front server - Apache (with mod_xsendfile) or lighttpd, by returning empty response with X-Sendfile header with path.

Nginx uses X-Accel-Redirect header for same functionality but you have to configure Rails properly in proper environment file:

config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect'

Rails 3 update: this line already exists in production.rb, just uncomment it.

Add sendfile on; to your nginx config to utilize header sent by Rails. Remember the absolute path must be used and nginx must have read access to file.

Another way for aliased files:

For better security I use aliases in nginx instead of absolute paths, however send_file method checks existence of file which fails with alias. Thus I changed my action to:

  head(
        'X-Accel-Redirect'=> file_item.location,
        'Content-Type' => file_item.content_type,
        'Content-Disposition' => "attachment; filename=\"#{file_item.name}\"");
  render :nothing => true;

Upvotes: 46

ashisrai_
ashisrai_

Reputation: 6568

In Rails 3, just uncomment the line config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' in production.rb inside environments folder.

Upvotes: 17

evaneykelen
evaneykelen

Reputation: 472

I've had similar issues with send_file() in the past, using send_data() instead saved me back then (e.g. send_data File.read(filename), :disposition => 'inline', :type => "some/mimetype")

Upvotes: 4

Related Questions