5

On LAMP stack, I can not get xSendFile to work. The problem is that the downloads have 0 bytes.

In order to be sure that I have xSendFile installed, I have added this to my .htaccess file:

<IfModule mod_xsendfile.c>
  SetEnv MOD_mod_xsendfile 1
</IfModule>

(I can not test this with apache_get_modules() because I am running PHP as fastCGI.)

I then have the following PHP code:

 if (1 != getenv('MOD_mod_xsendfile'))
    {
        echo 'mod_xsendfile is not installed';
    }
    else
    {
        $path = '/home/infar/';
        $file = 'hello.txt';
        if (file_exists($path.$file) && strlen($path.$file)>1)
        {
            if (true) // change to false to go around Yii
            {
                Yii::app()->request->xSendFile($path.$file,array(
                    'saveName'=> 'hello.txt',
                    'mimeType'=> 'plain/text',
                    'terminate'=> true,
                ));
            }
            else
            {
                //set proper Content-Type
                header('Content-Type: plain/text');
                //force download box with the filename hello.txt
                header('Content-Disposition: attachment;filename=hello.txt');
                header('X-Sendfile: $path.$file');
                exit;      
            }  
        }
        else
        {
            echo 'file not found at: '.$path.$file;
        }
    }

This code runs as expected; from which I conclude that xSendFile must be installed and/or enabled. I then run this script both with Yii xSendFile and with plain PHP. In both cases the download window opens, but always with 0 bytes.

I am at my wits' end. What am I doing wrong?

In the interest of full disclosure, here is the relevant part of httpd.conf:

<IfModule mod_xsendfile.c>
    XSendFile on
    XSendFilePath /home/infar
</IfModule>

Edit:

Output compression is turned off:

ini_get("zlib.output_compression"); // returns "Off"

Chrome shows me the following headers:

Cache-Control:no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Connection:Keep-Alive
Content-Disposition:attachment;filename=hello.txt
Content-Length:0
Content-Type:plain/text
Date:Sun, 23 Mar 2014 18:22:49 GMT
Expires:Thu, 19 Nov 1981 08:52:00 GMT
Keep-Alive:timeout=2, max=100
Pragma:no-cache
Server:Apache
X-Sendfile:/home/infar/hello.txt

Update March 26th, 2014

When testing @gabrieloliveira suggestion, I discovered the following:

header('Content-Type: plain/text');
header('Content-Disposition: attachment;filename=hello.txt');
header('X-Sendfile: '.$path.$file); // $path.$file outside quotes
header('Content-Length: '.filesize($path.$file)); // Filesize
echo '12345678901234567890';
exit;  

This script (where I have added echo '12345678901234567890';) produced a download for a file containing the following 16 character string 1234567890123456. 16 characters matches the filesize of hello.txt. So it seems we can conclude the following

  1. Content-Length & Filesize are handled correctly.
  2. Either X-SendFile does not find the file, or:
  3. X-SendFile does not process the file.

In fact, commenting out X-SendFile, like so:

// header('X-Sendfile: '.$path.$file);

produces the same result; indiscernible from executing the X-Sendfile header.

Ivo Renkema
  • 2,188
  • 1
  • 29
  • 40
  • No, never figured it out. I just gave up. Sorry I can not be of more help. I concluded that something my hoster (I am with Dreamhost) did just made this impossible... – Ivo Renkema Jan 04 '15 at 13:40
  • Any workaround? It used to work with my hosting provider (certified hosting) until we were moved to a new server. They claim that it's a script issue, I maintain that the server is misconfigured. I can't seem to get it to work with readfile() either. Granted, I've got an .htaccess file there to block requests with a referrer that's not from the domain but I doubt that's the issue. – Jan K. Jan 04 '15 at 19:33

2 Answers2

3

Test with pure PHP editing this part:

header('Content-Type: plain/text');
//force download box with the filename hello.txt
header('Content-Disposition: attachment;filename=hello.txt');
header('X-Sendfile: '.$path.$file); // $path.$file outside quotes
header('Content-Length: '.filesize($path.$file)); // Filesize

Edit:

Try change:

header('application/octet-stream');
gabrieloliveira
  • 560
  • 3
  • 10
2

Make sure the path to the file to serve is the same path you're using on the XSendFilePath directive on httpd.conf or httpd-vhosts.conf

Or: Are you sending files which are outside the public folder? If so then add XSendFileAllowAbove on to apache config

Meetai.com
  • 6,622
  • 3
  • 31
  • 38
  • Thanks. I am trying to send the file from outside the webroot. My `XSendFilePath` is `/home/infar`. Whether I try to send the a file from that directory, or its subdirectory makes no difference: always 0 bytes. As I understand it, `XSendFileAllowAbove` has been deprecated. – Ivo Renkema Mar 23 '14 at 11:23
  • You're right, it was deprecated. I also I'm delivering files from outside the webroot and managed to make it work by simply using the same path once the file is delivered. If you can get Chrome to tell you the Headers of the request, you can tell which file/path is being delivered. – Meetai.com Mar 23 '14 at 12:35
  • I have added the headers as shown by Chrome. Looks allright to me. Chrome does tell me that the GET request has the status "(cancelled)" – Ivo Renkema Mar 23 '14 at 18:21
  • ... while Firefox assures me that the same request has status 200 OK. – Ivo Renkema Mar 23 '14 at 18:34