Home » PHP » How to Download File from URL in PHP

How to Download File from URL in PHP

You may need to get file contents by URL quite often. For example, when you need to import some data from a third-party service, download an image or a backup. There are several ways to download files in PHP. You can use the file_get_contents() function, which stores the contents of any file into a variable, or you can use cURL library. With this library, you can download a file from a remote host into a variable or save it to the local file system.

In this short article, I will show you how to download file from URL in PHP. Additionally, I will address issues you may encounter.

Table of Contents

How to Get File from URL in PHP

I will use the Symfony framework source code as a file to download in the examples below. You can find a link to the latest version on the Releases page. At the time of writing, the size of the archive was around 11 megabytes. I will also use the symfony/stopwatch package to measure RAM usage and symfony/var-dumper to print variable values. Let’s install the libraries:

composer require symfony/stopwatch composer require symfony/var-dumper

Now let’s create a download.php file with the wrapping code that will determine how much memory was used in each case:

download.php<?php require "vendor/autoload.php"; use Symfony\Component\Stopwatch\Stopwatch; //uses $watch = new Stopwatch(); $watch->start("downloading"); //downloading code $metrics = $watch->stop("downloading"); dump("Metrics: " . (string) $metrics);

1. file_get_contents()

This is a function from the standard PHP library that is commonly used to read the contents of files, no matter where they are located. It can read local files and files on a remote host. It supports both HTTP and FTP protocols.

string$filenamePath to the file or URL
bool$use_include_pathSpecifies whether it should search files in the include_pathfalse
resource$contextAdditional parameters for network requestsnull
int$offsetNumber of bytes to skip before reading data0
int$lengthNumber of bytes to readnull

If the server does not require authorization or other special headers or cookies, you can simply provide the URL in the first parameter of the function. For example:

download.php$contents = file_get_contents("https://github.com/symfony/symfony/archive/refs/tags/v6.2.7.zip"); dump("Size: " . strlen($contents) / 1024 / 1024);

As a result, the $contents variable will contain the contents of the file if there are no errors or false. This function does not throw exceptions, so errors have to be handled manually. In this case, the maximum amount of memory used is slightly larger than the file size. It means that the files are loaded into memory.

You can get the last error code using the error_get_last() function. For example:

download.php$contents = file_get_contents("https://github.com/symfony/symfony/archive/refs/tags/v6.2.7-non-existent.zip"); if ($contents === false) {     $error = error_get_last();     dump($error); }else{     dump("Size: " . strlen($contents) / 1024 / 1024); }

If the remote host requires additional headers or authorization to download a file, you can provide them using the $context parameter. Simply create an array with the required parameters and build a context using the stream_context_create() function. For example, if you need to provide the Accept-Language header with the value en_US, you can use the following code:

download.php$options = [     "http" => [         "method" => "GET",         "header" => "Accept-Language: en_US\r\n"     ], ]; $context = stream_context_create($options); $contents = file_get_contents(     "https://github.com/symfony/symfony/archive/refs/tags/v6.2.7.zip",     false,     $context ); dump("Size: " . strlen($contents) / 1024 / 1024);

You can find more information about using context in the official documentation. As you can see, the file_get_contents function has several disadvantages.

  • The entire contents of the file are loaded into memory; this is an issue for large files.
  • The function does not throw exceptions, so error handling is more complicated.
  • Configuring headers using context is quite complicated.

Please note that Laravel, and possibly other frameworks, intercept such PHP errors and warnings and convert them to ErrorException, which can then be caught with a try … catch block. But by default, file_get_contents() does not throw any exceptions.

If you do not need to modify the uploaded data, you can combine file_get_contents() with file_put_contents(). In this case, the PHP interpreter will perform optimization and write the data directly to the file without loading it into memory:

download.phpfile_put_contents(     "source.zip",     file_get_contents(         "https://github.com/symfony/symfony/archive/refs/tags/v6.2.7.zip"     ) );

However, there is a more elegant way to avoid all the disadvantages described above. This is the cURL library and the guzzle/guzzlehttp package.

2. guzzle/guzzlehttp

The guzzle/guzzlehttp package is an object-oriented interface for making HTTP requests via cURL. However, it requires ext-curl PHP extension to be installed on your server. Then you can install the package using the following command:

composer require guzzlehttp/guzzle

Then, simply create a client and call the get() method to download a file via URL into the variable. For example:

download.phpuse GuzzleHttp\Client;

download.php$client = new Client(); $response = $client->get(     "https://github.com/symfony/symfony/archive/refs/tags/v6.2.7.zip" ); $contents = $response->getBody()->getContents(); dump("Size: " . strlen($contents) / 1024 / 1024);

If something goes wrong during the request, the client will throw an exception. If something goes wrong, the client will throw an exception. If cURL can’t connect to the server, a ConnectException will be thrown, and if the server returns an error code, a RequestException will be thrown. You can catch and handle these errors using a try … catch block.

download.phpuse GuzzleHttp\Exception\ConnectException; use GuzzleHttp\Exception\RequestException; use GuzzleHttp\Client;

download.php$client = new Client(); try {     $response = $client->get(         "https://github.com/symfony/symfony/archive/refs/tags/v6.2.7.9999.zip"     ); } catch (RequestException $e) {     $response = $e->getResponse();     dump($e->getMessage()); } catch (ConnectException $e) {     dump($e->getMessage()); }

Another advantage of this package is that it allows you to upload files directly to the file system. You need to create a writable file resource and provide it to the client in the sink option. For example, let’s create a temporary file in the /tmp directory and sink data into it:

download.phpuse GuzzleHttp\Client; use GuzzleHttp\RequestOptions;

download.php$tempFileName = tempnam("/tmp/", "source"); $tempFile = fopen($tempFileName, "w"); $client = new Client(); $response = $client->get(     "https://github.com/symfony/symfony/archive/refs/tags/v6.2.7.zip",     [         RequestOptions::SINK => $tempFile,     ] );

This code works in the same way, but file contents are saved to a file without loading it into memory if there are no errors. Moreover, you don’t need to close the resource; cURL does it.

Thus, this library allows you to configure additional parameters such as headers in a much simpler way compared to file_get_contents.

3. Laravel HTTP Facade

If you’re using Laravel, there is an HTTP facade that makes using the cURL library even easier. It is usually used to make API requests, but it can also download files. Let’s create a DownloadFileCommand, to test how it works in Laravel:

php artisan make:command DownloadFileCommand

The easiest way to download the file from the URL looks like this:

app/Console/Commands/DownloadFileCommand.php$watch = new \Symfony\Component\Stopwatch\Stopwatch(); $watch->start("downloading"); $contents = \Http::get(     "https://github.com/symfony/symfony/archive/refs/tags/v6.2.7.zip" )->body(); dump("Size: " . strlen($contents) / 1024 / 1024); $metrics = $watch->stop("downloading"); dump("Metrics: " . (string) $metrics);

This client doesn’t throw RequestException, but returns a response object, which can be used to determine whether the request was successfully executed or not.

successful()Returns true if the request is successful
failed()Returns true if any HTTP error occurred
clientError()Returns true if HTTP status code is 4xx
serverError()Returns true if HTTP status code is 5xx

For example, the code above shows how to handle a 404 error:

app/Console/Commands/DownloadFileCommand.php$watch = new \Symfony\Component\Stopwatch\Stopwatch(); $watch->start("downloading"); $response = \Http::get(     "https://github.com/symfony/symfony/archive/refs/tags/v6.2.7.999.zip" ); if ($response->successful() === false) {     dump($response->body()); } $contents = $response->body(); dump("Size: " . strlen($contents) / 1024 / 1024); $metrics = $watch->stop("downloading"); dump("Metrics: " . (string) $metrics);

the following example shows how to save the contents of a file to a local file without saving it to memory. Call the sink() method and provide the file path where you want to save data as the first argument:

app/Console/Commands/DownloadFileCommand.php$watch = new \Symfony\Component\Stopwatch\Stopwatch(); $watch->start("downloading"); $tempFileName = tempnam("/tmp/", "source"); $response = \Http::sink($tempFileName)->get(     "https://github.com/symfony/symfony/archive/refs/tags/v6.2.7.zip" ); $metrics = $watch->stop("downloading"); dump("Metrics: " . (string) $metrics);

The response object can be used to determine whether the file was loaded without errors as well as to read the contents of the file into a variable, but by default, the file will not be loaded into RAM until you call the body() method. More information about this client can be found in the official documentation.

Wrapping Up

In this article, we’ve covered how to download file from URL in PHP using different methods and libraries. A function from a standard PHP library can be used in simple cases, but if you need to provide additional headers or other settings, you should choose cURL, Guzzle, or a client from Laravel.

3 thoughts on “How to Download File from URL in PHP”

  1. For training, it is a useless educational presentation. The fact that you are a good programmer – and that you assume the reader is – is a joke to present online. Those of us who CAN’T do this need real examples that can be downloaded – and understood. It was messy and fast from start to finish – a pure mockery of those of us who are in the start-up phase.


Leave a Comment