Question

I have a php script (actually https://drupal.org/project/file_force) that is forcing users who click on a link to download that link by adding the correct headers to the response.

This link works fine 90% of the time. Occasionally the incorrect content-length is being passed so users are getting apparently truncated files. The mistake happens consistently on particular files, but if those files are re-uploaded, the error may not appear on the new instance, which makes me think this is not an issue with the files, but instead a cache somewhere. So I ran clearstatcache() every time to no avail. What is odd is that php is passing the correct file size, or says it is when I pass the string it's inserting to a log file.

Here's the relevant code:

  clearstatcache();
  return array(
    'Content-Type: ' . $mimeinfo,
    'Content-Disposition: ' . $disposition . '; filename="' . basename($filepath) . '";',
    // Content-Length is also a good header to send, as it allows the browser to
    // display a progress bar correctly.
    // There's a trick for determining the file size for files over 2 GB. Nobody
    // should be using this module with files that large, but… the sprintf()
    // trickery makes sure the value is correct for files larger than 2GB. See
    // note at http://php.net/filesize
    'Content-Length: ' . sprintf('%u', filesize($filepath)),
  );

A sample output from sprintf('%u', filesize($filepath)) on a file that isn't working is 2682059 which somehow gets translated to 1740048 when the browser gets to see it.

I've tried removing the sprintf function to no avail. I've also tried not including a Content-Length declaration at all, but someone one is getting attached with the incorrect value anyway. This last piece of evidence perhaps suggests some other code is overriding the content headers I'm setting here, yet it appears to be leaving alone any other headers that I change in the above code to test that theory.

Any thoughts for where to look?

Was it helpful?

Solution

I resolved the issue.

Turns out another module within Drupal was adding its own content-length header and getting the value from a database rather than the file directly (weird), and it was happening down stream. By reversing the order that the modules got their hands on the headers, the issue went away. I have filed a bug report against the offending module.

OTHER TIPS

SOURCES: PHP official documentation - "header" function

There's a function in PHP called header that you would like to use to set headers, before the actual page loads:

Here's the skeleton of the function:

void header ( string $string [, bool $replace = true [, int $http_response_code ]] )

Explanation:

Parameters:

  • string

The header string.

There are two special-case header calls. The first is a header that starts with the string "HTTP/" (case is not significant), which will be used to figure out the HTTP status code to send. For example, if you have configured Apache to use a PHP script to handle requests for missing files (using the ErrorDocument directive), you may want to make sure that your script generates the proper status code.

  • replace

The optional replace parameter indicates whether the header should replace a previous similar header, or add a second header of the same type. By default it will replace, but if you pass in FALSE as the second argument you can force multiple headers of the same type.

  • http_response_code

Forces the HTTP response code to the specified value. Note that this parameter only has an effect if the string is not empty.

===

Return Values:

No value is returned.


Example:

<?php
    header('Location: http://www.example.com/'); //redirect to www.example.com
?>

Note: The function must be called before any output is sent, i.e. before any HTML tags or before any echo calls.

in your case:

<?php
    header('Content-Length: ' . sprintf('%u', filesize($filepath)));
?>

the replace parameter which is by default - true, will cause your program to overwrite all previously set 'Content-Length' headers..

Hope all the explanation was worth it... :)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top