2

I have a remote link from which I want to force download a file. Right now I am using cURL.

My script works for this url (as I tested a different one): http://images.mob.org/iphonegame_img/rip_curl_surfing_game_live_the_search/real/1_rip_curl_surfing_game_live_the_search.jpg

But I need it to work on this one: https://googledrive.com/host/0B_3oJnpnNoF9UjlkVUwtWE5CY0U/city.jpg

And here is my PHP code

 <?php
    $file = 'http://images.mob.org/iphonegame_img/rip_curl_surfing_game_live_the_search/real/1_rip_curl_surfing_game_live_the_search.jpg';
    download($file,2000);

    function download($file,$chunks){
        set_time_limit(0);
        header('Content-Description: File Transfer');
        header('Content-Type: application/octet-stream');
        header('Content-disposition: attachment; filename='.basename($file));
        header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
        header('Expires: 0');
        header('Pragma: public');
        $size = get_size($file);
        header('Content-Length: '.$size);

        $i = 0;
        while($i<=$size){
            //Output the chunk
            get_chunk($file,(($i==0)?$i:$i+1),((($i+$chunks)>$size)?$size:$i+$chunks));
            $i = ($i+$chunks);
        }

    }

    //Callback function for CURLOPT_WRITEFUNCTION, This is what prints the chunk
    function chunk($ch, $str) {
        print($str);
        return strlen($str);
    }

    //Function to get a range of bytes from the remote file
    function get_chunk($file,$start,$end){
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $file);
        curl_setopt($ch, CURLOPT_RANGE, $start.'-'.$end);
        curl_setopt($ch, CURLOPT_BINARYTRANSFER, 1);
        curl_setopt($ch, CURLOPT_WRITEFUNCTION, 'chunk');
        $result = curl_exec($ch);
        curl_close($ch);
    }

    //Get total size of file
    function get_size($url){
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_HEADER, true);
        curl_setopt($ch, CURLOPT_NOBODY, true);
        curl_exec($ch);
        $size = curl_getinfo($ch, CURLINFO_CONTENT_LENGTH_DOWNLOAD);
        return intval($size);
    }

    ?>

I don't know what to do know. Please advice something.

Barmar
  • 741,623
  • 53
  • 500
  • 612
Davit
  • 1,394
  • 5
  • 21
  • 47
  • And what is exactly your problem? – René Höhle Jan 04 '13 at 01:28
  • A file download starts which is 0 bytes if I use a link which I want. – Davit Jan 04 '13 at 01:29
  • Why do you need a complex code for a simple task ? Why chunks ? – Baba Jan 04 '13 at 01:30
  • For a better performance. I tried simpler other methods but they all failed. – Davit Jan 04 '13 at 01:31
  • What do you mean it all failed – Baba Jan 04 '13 at 01:31
  • `https://googledrive.com/host/0B_3oJnpnNoF9UjlkVUwtWE5CY0U/city.jpg` this link did not download, it gave 0 bytes, file was empty. but another file worked. – Davit Jan 04 '13 at 01:32
  • It looks like googledrive.com ignores the `Range` request-header, so it's returning the entire file. But I don't see why that would cause you to get 0 bytes. – Barmar Jan 04 '13 at 01:36
  • `http://gdurl.com/` this website has it done, in addition they have `open` and `download` choices. there is a demo there so you can see it. I want something similar at last. – Davit Jan 04 '13 at 01:39
  • Another thing about googledrive.com: It doesn't return a Content-Length header for this document, which your `get_size()` function requires. – Barmar Jan 04 '13 at 01:42

2 Answers2

21

You don'r need a complex code for a simple task, this should work fine

$file = 'https://googledrive.com/host/0B_3oJnpnNoF9UjlkVUwtWE5CY0U/city.jpg';
download($file);

function download($url) {
    set_time_limit(0);
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
    $r = curl_exec($ch);
    curl_close($ch);
    header('Expires: 0'); // no cache
    header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
    header('Last-Modified: ' . gmdate('D, d M Y H:i:s', time()) . ' GMT');
    header('Cache-Control: private', false);
    header('Content-Type: application/force-download');
    header('Content-Disposition: attachment; filename="' . basename($url) . '"');
    header('Content-Transfer-Encoding: binary');
    header('Content-Length: ' . strlen($r)); // provide file size
    header('Connection: close');
    echo $r;
}
Baba
  • 94,024
  • 28
  • 166
  • 217
  • 2
    It is great solution, one of the many that really works, but the bad part - the download starts on client side only after it downloaded on the server, can image be downloaded directly from the source? – IgorCh Nov 05 '13 at 10:12
0

Assuming you aren't also having SSL certificate issues, the issue is that Google doesn't appear to return content-length for HEAD requests.

So remove the CURLOPT_NOBODY line, and the script works.

Edit: Barmar already mentioned the content-length issue with Google Drive above, but the solution still applies.

Community
  • 1
  • 1
user1936123
  • 216
  • 2
  • 11