I'm using mod_xsendfile (v0.12) to serve static files where Django is controlling access to the files based on users and permissions.
In my conf file, I have:
XSendFile On
XSendFilePath e:/documents/
<Directory e:/Documents>
Order allow,deny
Allow from all
</Directory>
In my django code, I set the headers like so:
assert(isinstance(filename, FieldFile))
xsendfile = filename.name
if(platform.system() == 'Windows'):
xsendfile = xsendfile.replace('\\', '/')
response = HttpResponse()
response['X-Sendfile'] = xsendfile
mimetype = mimetypes.guess_type(xsendfile)[0]
response['Content-Type'] = mimetype
response['Content-Length'] = filename.size
And in my log file I get:
[Fri Oct 22 08:54:22 2010] [error] [client 192.168.20.34] (20023)The given path
was above the root path: xsendfile: unable to find file:
e:/Documents/3/2010-10-20/TestDocument.pdf
In this version of mod_xsendfile
,
XSendFileAllowAbove On
generates the error:
Invalid command 'XSendFileAllowAbove', perhaps misspelled or defined by a module
not included in the server configuration
I assumed that was because they have added the XSendFilePath
white list. Anyone else got this to work?
I've had a whole lot of trouble most of the times I had to configure XSendfile path.
Here is me testing several scenarios on Windows to see what was wrong (jump to the end of this post to see the conclusion ad recommendations):
<?php
/* X-SENDFILE
* This can be a b*tch to configure. So I'm writing various scenarios here so that I can rely on them in the future.
* Example use: after re-installing XAMPP, after changing config file, in a new script, after some time without using it...
* Tested on Windows 7 + XAMPP (Apache/2.4.3, PHP/5.4.7) + mod_xsendfile 1.0-P1 for Apache 2.4.x Win32
*/
/** Environment Debug **/
//echo dirname(__FILE__); die();
//echo $_SERVER['DOCUMENT_ROOT']; die();
/** The damn fucking path, with comments **/
// Local file in execution directory.
// Tested with: *no* XSendFilePath inside of the Apache config
// Result: works fine.
//header("X-Sendfile: " . 'localfile.zip' );
// Local file in execution directory + relative path
// Tested with: *no* XSendFilePath inside of the Apache config
// Result: works fine.
//header("X-Sendfile: " . '../xsendfile/localfile.zip' );
// Local file in execution directory + absolute pathS
// Tested with: *no* XSendFilePath inside of the Apache config
// Result: works fine and a lot of flexibility on the slash and letter drive format combinations *BUT* case-sensitive
//header("X-Sendfile: " . 'D:\Dropbox\XAMPP\web\tests\Languages\Apache\xsendfile\localfile.zip' ); // works fine
//header("X-Sendfile: " . '\Dropbox\XAMPP\web\tests\Languages\Apache\xsendfile\localfile.zip' ); // works fine
//header("X-Sendfile: " . 'D:/Dropbox/XAMPP/web/tests/Languages/Apache/xsendfile/localfile.zip' ); // works fine
//header("X-Sendfile: " . '/Dropbox/XAMPP/web/tests/Languages/Apache/xsendfile/localfile.zip' ); // works fine
//header("X-Sendfile: " . '/dropbox/XAMPP/web/tests/Languages/Apache/xsendfile/localfile.zip' ); // FAILS (case-sensitive)
// File in the XSendFilePath directory + Absolute path
// Tested with: XSendFilePath D:\Dropbox\XAMPP\web -- Mind the backward slashes
// Result: FAILS! error.log => [Wed Feb 20 19:08:02.617971 2013] [:error] [pid 15096:tid 1768] (20023)The given path was above the root path: [client ::1:56658] xsendfile: unable to find file: D:\\Dropbox\\XAMPP\\web\\xsfile.zip
//header("X-Sendfile: " . 'D:\Dropbox\XAMPP\web\xsfile.zip' );
// File in the XSendFilePath directory + Absolute path
// Tested with: XSendFilePath D:/Dropbox/XAMPP/web <== mind the forward slashes this time
// Result: WORKS! Conclusion: XSendFilePath needs use forward slashes on Windows AND we don't need any trailing slash in it.
header("X-Sendfile: " . 'D:\Dropbox\XAMPP\web\xsfile.zip' );
/** We might wanna test also:
* - How does backward slashes in both XSendfilePath and the header combine?
* - The use of subdirectories.
* /
/** The rest of the headers (until otherwise stated, nothing special) **/
header("Content-Type: application/zip");
header("Content-Disposition: attachment; filename=\"" . 'blah.zip' . "\"");
header("Content-Transfer-Encoding: binary");
header("Pragma: public");
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Cache-Control: public");
header("Content-Description: File Transfer");
/** Tell the script to stop (so the file download may start) **/
die();
?>
So, basically, for X-Sendfile on Windows, make sure that:
Hope it helps someone! Fabien
Do not set a Content-Length yourself. This will only confuse handlers such as mod_wsgi in this case. mod_xsendfile will itself set the correct Content-Length.
On Windows you must not only provide the drive letter, the drive letter must be actually in upper-case (IIRC)!
I have a working test configuration like so:
<Directory "E:/">
XSendFile on
XSendFilePath E:/localhosts
</Directory>
One of my working test scripts in E:/Apache2.2/htdocs/ looks like this:
<?php
header('X-SendFile: E:/localhosts/archive.tar.bz2');
header('Content-type: application/octet-stream');
header('Content-disposition: attachment; filename="blob"');
?>
XSendFileAllowAbove was removed a while back in favor of XSendFilePath
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With