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

How to Download File from URL in PHP

Downloading files is a very common task. You may need to do this when you want to import data from another site, download a backup, and so on. There are multiple methods you can use to download a file. You can use the built-in file_get_contents() function which fetches the contents of any file into a string, or you can use an external library that is called cURL. This library can download a remote file directly to a file in your file system.

In this short article, we will dive into how to download file from URL in PHP using these methods and show potential issues you may face.

How to Get File from URL in PHP

In the examples, I will download the source code of the Symfony framework. You can find its files here. The latest release at the moment of writing its file size is 11 MB. Also, I will use the symfony/stopwatch package to measure memory consumption.

1. file_get_contents()

As I mentioned before, it is a built-in function that is usually used for reading the contents of any file in the local filesystem into a string. However, it can also work with HTTP and FTP protocols, so you can use it to download remote files. Here are the arguments that can be passed into that function:

string$filenamePath to the file or URL
bool$use_include_pathSpecified 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 your server does not require specific authentication, cookies, or headers, you can simply pass the URL as the first parameter. For example:

$contents = file_get_contents("https://github.com/symfony/symfony/archive/refs/tags/v6.2.7.zip");

The $contents variable will contain the content of the file or false when something goes wrong. The function does not throw an exception, so you need to handle errors manually. You can retrieve the last error using the error_get_last() function:

if ($data === false) { $error = error_get_last(); //do something }

For example, if PHP can not resolve the domain name, you will get this error:

If there are no errors, you can save the content into a local file:


But when your server requires additional authentication or a specific request method things become more complicated. You can use the context resource to specify these values. Just create an array with required settings and create the context using the stream_context_create() function. For example, if you want to send a header with the name of Accept-Language and a value of en_US, you can use this code:

$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 );

You can find other available settings in the official documentation. As you can see, this function has multiple issues:

  • It loads everything into the memory and this can a problem when you are downloading large files.
  • It does not throw exceptions, so error handling is more difficult.
  • Additional configuration is complex.

Note, that Laravel, and maybe other frameworks have their own error handlers, which convert PHP errors into ErrorException. In this case, you can catch it using try..catch block. But the default behavior of file_get_contents() and fopen() functions – do not throw any errors and return false as the result.

If you do not need to change the downloaded data, you can pass the result of the file_get_contents() directly into the file_put_contents() function. In this case, PHP does its magic and saves the data directly into the file without first saving it into a variable. This reduces memory consumption:

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

But there are more convenient ways to avoid all these issues. You can use cURL and its object-oriented wrappers, such as the guzzle/guzzlehttp library.

2. guzzle/guzzlehttp

The guzzlehttp/guzzle package has a fluent interface that can help to perform HTTP requests more easily. But you need to have the ext-curl PHP extension installed. Once you have installed the extension, you can install the package using Composer:

composer require guzzlehttp/guzzle

I have described this library in detail in the article about making queries to external APIs. You can read it first to get more information.

If you want to download the file into a string, just create a client and make the request:

use GuzzleHttp\Client; $client = new Client(); $response = $client->get( "https://github.com/symfony/symfony/archive/refs/tags/v6.2.7.zip" ); $contents = $response->getBody()->getContents();

If something goes wrong, the client will throw an exception. This can be a ConnectException when cURL can’t connect to the server or a RequestException when the server responds with a non-success status code. You can handle these exceptions using the try…catch block:

try { $response = $client->get( "https://github.com/symfony/symfony/archive/refs/tags/v6.2.7.zip" ); } catch (RequestException $e) { $response = $e->getResponse(); //Do something } catch (ConnectException $e) { //Do something }

Another advantage of this package is the ability to download a file directly into a file in the local filesystem. Just create a stream resource and use the sink option to save the response result into it. In this example I will create a temporary file in the /tmp directory:

use GuzzleHttp\Client; use GuzzleHttp\RequestOptions; //... $tempFileName = tempnam("/tmp/"); $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, ] );

If no errors occur, the contents of the file will be in the temporary file. Otherwise ConnectException or RequestException will be thrown. You don’t need to close the resource, curl does it by itself.

In addition, this library allows you to configure additional HTTP options very easily. You can add basic authentication, headers, cookies, and more just like the path to the file was added above.

3. Laravel Http Client

If you are using Laravel framework, you can utilize its own wrapper for Guzzle and cURL. It is called Http and is designed for API queries, but you can download files using it. The simplest code to download a file from a URL would look like this:

use Illuminate\Support\Facades\Http; $contents = Http::get( "https://github.com/symfony/symfony/archive/refs/tags/v6.2.7.zip" )->body();

This client does not throw any exceptions but returns a response object. You can use these methods of the object to determine whether the request was successful 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:

$response = Http::get("https://github.com/symfony/symfony/archive/refs/tags/v6.2.7.zip"); if($response->successful() === false){ //Do something } $contents = $response->body();

After this, you can save the file into the storage or do anything that you want to. If you want to download a file directly into the local file, you can use the sink() method. This method wants only the file name as the first argument:

$tempFileName = tempnam("/tmp/", "source"); $response = Http::sink($tempFileName)->get( "https://github.com/symfony/symfony/archive/refs/tags/v6.2.7.zip" );

The response can be used to determine whether the request was successful, or to fetch file body. But the file will not be loaded into the memory until you call the body() method. You can find more information about using the client in the official documentation.

Wrapping Up

In this article, I have explained how to download file in PHP using different methods. The built-in function can be used for downloading files in simple cases, but if you need to specify additional settings, it is better to use cURL or Guzzle. Which method do you prefer? Let me know in the comments section below.

Your Reaction

Leave a Comment