Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to download a file using curl in php?

Tags:

php

curl

How can I use Curl to download a file in PHP if the headers are set to true? can I also get the filename and extension of file?

Example PHP code:

curl_setopt ($ch, CURLOPT_HEADER, 1);
$fp = fopen($strFilePath, 'w');
curl_setopt($ch, CURLOPT_FILE, $fp);
like image 849
johnlemon Avatar asked May 30 '11 14:05

johnlemon


People also ask

How do I download a file using curl?

How to download a file with curl command. The basic syntax: Grab file with curl run: $ curl https://your-domain/file.pdf. Get file using ftp or sftp protocol: $ curl ftp://ftp-your-domain-name/file.tar.gz.

Where does curl download to?

The remote file name to use for saving is extracted from the given URL, nothing else. Consequentially, the file will be saved in the current working directory. If you want the file saved in a different directory, make sure you change current working directory before you invoke curl with the -O, --remote-name flag!


4 Answers

Download file or web page using PHP cURL and save it to file

<?php
/**
* Initialize the cURL session
*/
$ch = curl_init();
/**
* Set the URL of the page or file to download.
*/
curl_setopt($ch, CURLOPT_URL,
'http://news.google.com/news?hl=en&topic=t&output=rss');
/**
* Create a new file
*/
$fp = fopen('rss.xml', 'w');
/**
* Ask cURL to write the contents to a file
*/
curl_setopt($ch, CURLOPT_FILE, $fp);
/**
* Execute the cURL session
*/
curl_exec ($ch);
/**
* Close cURL session and file
*/
curl_close ($ch);
fclose($fp);
?>
like image 93
Sujit Agarwal Avatar answered Sep 26 '22 04:09

Sujit Agarwal


I belie you have found your answer by now. However, I'd like to share my script that works well by sending a json request to a server which returns the file in binary, then it downloads on the fly. Saving is not necessary. Hope it helps!

NOTE: You can avoid converting the post data to json.

<?php

// Username or E-mail
$login = 'username';
// Password
$password = 'password';
// API Request
$url = 'https://example.com/api';
// POST data
$data = array('someTask', 24);
// Convert POST data to json
$data_string = json_encode($data);
// initialize cURL
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL,$url);
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
curl_setopt($ch, CURLOPT_USERPWD, "$login:$password");
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_POSTFIELDS, $data_string);
curl_setopt($ch, CURLOPT_HEADER, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);

// Execute cURL and store the response in a variable
$file = curl_exec($ch);

// Get the Header Size
$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
// Get the Header from response
$header = substr($file, 0, $header_size);
// Get the Body from response
$body = substr($file, $header_size);
// Explode Header rows into an array
$header_items = explode("\n", $header);
// Close cURL handler
curl_close($ch);

// define new variable for the File name
$file_name = null;

// find the filname in the headers.
if(!preg_match('/filename="(.*?)"/', $header, $matches)){
    // If filename not found do something...
    echo "Unable to find filename.<br>Please check the Response Headers or Header parsing!";
    exit();
} else {
    // If filename was found assign the name to the variable above 
    $file_name = $matches[1];
}
// Check header response, if HTTP response is not 200, then display the error.
if(!preg_match('/200/', $header_items[0])){
    echo '<pre>'.print_r($header_items[0], true).'</pre>';
    exit();
} else {
    // Check header response, if HTTP response is 200, then proceed further.

    // Set the header for PHP to tell it, we would like to download a file
    header('Content-Description: File Transfer');
    header('Content-Type: application/octet-stream');
    header('Content-Transfer-Encoding: binary');
    header('Expires: 0');
    header('Cache-Control: must-revalidate');
    header('Pragma: public');
    header('Content-Disposition: attachment; filename='.$file_name);

    // Echo out the file, which then should trigger the download
    echo $file;
    exit;
}

?>
like image 32
Attila Antal Avatar answered Sep 26 '22 04:09

Attila Antal


Below is a complete example that uses a class. The header parsing is more elaborate then it can be, cause I was laying the base for full hierarchial header storage.

I just noticed init() should reset a lot more variables if it wants to be possible to reuse the instance for more URL's, but this should at least give you a base of how to download a file to a filename sent by the server.

<?php
/*
 * vim: ts=4 sw=4 fdm=marker noet tw=78
 */
class curlDownloader
{
    private $remoteFileName = NULL;
    private $ch = NULL;
    private $headers = array();
    private $response = NULL;
    private $fp = NULL;
    private $debug = FALSE;
    private $fileSize = 0;

    const DEFAULT_FNAME = 'remote.out';

    public function __construct($url)
    {
        $this->init($url);
    }

    public function toggleDebug()
    {
        $this->debug = !$this->debug;
    }

    public function init($url)
    {
        if( !$url )
            throw new InvalidArgumentException("Need a URL");

        $this->ch = curl_init();
        curl_setopt($this->ch, CURLOPT_URL, $url);
        curl_setopt($this->ch, CURLOPT_HEADERFUNCTION,
            array($this, 'headerCallback'));
        curl_setopt($this->ch, CURLOPT_WRITEFUNCTION,
            array($this, 'bodyCallback'));
    }

    public function headerCallback($ch, $string)
    {
        $len = strlen($string);
        if( !strstr($string, ':') )
        {
            $this->response = trim($string);
            return $len;
        }
        list($name, $value) = explode(':', $string, 2);
        if( strcasecmp($name, 'Content-Disposition') == 0 )
        {
            $parts = explode(';', $value);
            if( count($parts) > 1 )
            {
                foreach($parts AS $crumb)
                {
                    if( strstr($crumb, '=') )
                    {
                        list($pname, $pval) = explode('=', $crumb);
                        $pname = trim($pname);
                        if( strcasecmp($pname, 'filename') == 0 )
                        {
                            // Using basename to prevent path injection
                            // in malicious headers.
                            $this->remoteFileName = basename(
                                $this->unquote(trim($pval)));
                            $this->fp = fopen($this->remoteFileName, 'wb');
                        }
                    }
                }
            }
        }

        $this->headers[$name] = trim($value);
        return $len;
    }
    public function bodyCallback($ch, $string)
    {
        if( !$this->fp )
        {
            trigger_error("No remote filename received, trying default",
                E_USER_WARNING);
            $this->remoteFileName = self::DEFAULT_FNAME;
            $this->fp = fopen($this->remoteFileName, 'wb');
            if( !$this->fp )
                throw new RuntimeException("Can't open default filename");
        }
        $len = fwrite($this->fp, $string);
        $this->fileSize += $len;
        return $len;
    }

    public function download()
    {
        $retval = curl_exec($this->ch);
        if( $this->debug )
            var_dump($this->headers);
        fclose($this->fp);
        curl_close($this->ch);
        return $this->fileSize;
    }

    public function getFileName() { return $this->remoteFileName; }

    private function unquote($string)
    {
        return str_replace(array("'", '"'), '', $string);
    }
}

$dl = new curlDownloader(
    'https://dl.example.org/torrent/cool-movie/4358-hash/download.torrent'
);
$size = $dl->download();
printf("Downloaded %u bytes to %s\n", $size, $dl->getFileName());
?>
like image 41
Mel Avatar answered Sep 23 '22 04:09

Mel


To get both headers and data, separately, you typically use both a header callback and a body callback. Like in this example: http://curl.haxx.se/libcurl/php/examples/callbacks.html

To get the file name from the headers, you need to check for a Content-Disposition: header and extract the file name from there (if present) or just use the file name part from the URL or similar. Your choice.

like image 44
Daniel Stenberg Avatar answered Sep 24 '22 04:09

Daniel Stenberg