2

I have to write a php script to download potentially large files. The file I'm reporting here works fine most of the times. However, if the client's connection is slow the request ends (with status code 200) in the middle of the downloading, but not always at the very same point, and not at the very same time. I tried to overwrite some php.ini variables (see the first statements) but the problem remains. I don't know if it's relevant but my hosting server is SiteGround, and for simple static file requests, the download works fine also with slow connections.

I've found Forced downloading large file with php but I didn't understand mario's answer. I'm new to web programming.

So here's my code.

    <?php
    ini_set('memory_limit','16M');
    ini_set('post_max_size', '30M');
    set_time_limit(0);

    include ('../private/database_connection.php');


    $downloadFolder = '../download/';
    $fileName = $_POST['file'];
    $filePath = $downloadFolder . $fileName;
    if($fileName == NULL)
    {
        exit;
    }
    ob_start();
    session_start();


    if(!isset($_SESSION['Username']))
    {
        // or redirect to login (remembering this download request)
        $_SESSION['previousPage'] = 'download.php?file=' . $fileName;
        header("Location: login.php");
        exit;
    }

    if (file_exists($filePath))
    {               
        header('Content-Description: File Transfer');
        header('Content-Type: application/octet-stream');
        //header('Content-Disposition: attachment; filename='.$fileName);
        header("Content-Disposition: attachment; filename=\"$fileName\"");
        header('Content-Transfer-Encoding: binary');
        header('Expires: 0');
        header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
        //header('Pragma: public');
        header('Content-Length: ' . filesize($filePath));
        ob_clean();
        flush();

        // download
        // 1
//      readfile($filePath);
        // 2
        $file = @fopen($filePath,"rb");
        if ($file) {
          while(!feof($file)) {
            print(fread($file, 1024*8));
            flush();
            if (connection_status()!=0) {
              @fclose($file);
              die();
            }
          }
          @fclose($file);
        }
        exit;
    }
    else
    {
        header('HTTP/1.1 404 File not found');
        exit;
    }
?>
Community
  • 1
  • 1
dnaxxx
  • 175
  • 1
  • 8
  • Have a look at http://stackoverflow.com/questions/597159/sending-large-files-reliably-in-php, there is one answer about the `X-SENDFILE` header. However, check if your host supports this header. – Joost Dec 28 '10 at 18:58
  • I don't think it's relevant but I'm using ASIHTTPRequest library to make the request. I build an ASIFormDataRequest and set a downloadDestinationPath. – dnaxxx Dec 28 '10 at 21:53
  • It seems that x-sendfile is not supported because the response arrives immediately, with status code 200, but without the file. I'm going to ask Siteground whether I can enable it though. – dnaxxx Dec 28 '10 at 22:36

1 Answers1

2

HTTP Status Code of 200 means everything executed okay.

Check that your Content-Length is correct, and try without the connection_status() function. It may be reporting improperly that the user disconnected. PHP has a connection_aborted() function which may be what you need.

Update: Because you asked about Mario's answer, what he means is that you should redirect your user to the actual file on the server and let your sever handle it instead of attempting to duplicate what the server can already do. In other words, you should use header("Location: " . $filePath), or a link to the filepath instead of using your script. The disadvantage is that you can't use the attachment style download, but you can open it in a new window, which will have a similar effect.

Malfist
  • 31,179
  • 61
  • 182
  • 269
  • you should add your comment to your answer. I'm almost positive that what happens is the connection has a bitflag set (for waiting perhaps) and therefore is no longer equal to the number 0. Then I'll give you a +1 for your answer. – sholsinger Dec 28 '10 at 18:58
  • I've checked the response's headers and the content-length is correct. Also, commenting out the whole if statement (that checks connection_status) doesn't change the behavior. – dnaxxx Dec 28 '10 at 21:50
  • Redirecting doesn't work (returns 404) as the file is not public I guess. Maybe, in the other topic, Mario was suggesting a workaround but that's what I didn't understand. ...I'm still at the starting point. Maybe there's something bad with my host? – dnaxxx Dec 28 '10 at 22:43
  • 1
    I don't think it would be a problem with your host. You can make the file public, but filter access to the folder that contains the file. You can do it through .htaccess, or you can make a folder that symlinks (shortcut for you windows folks) to the protected folder. – Malfist Dec 29 '10 at 14:09
  • Using temporary symlinks should be a good workaround. I thought I could avoid it. I didn't think sending a file was so hard. – dnaxxx Dec 30 '10 at 11:00