Download a file using Symfony's HttpClient component
Create a command to download a file with a nice progress bar using Symfony's HttpClient component
We have seen a previous post how to use Symfony’s HttpClient Component to get weather forecast by sending a GET request to and API endpoint, here is the post
Symfony Console Component in standalone mode
Symfony Console Component in standalone mode You can create a command within a Symfony Project easily by requiring symfony/console component and then creating a class that extends Symfony\Component\Console\Command\Command but does this means that you have to have a Symfony project in order to create a command line with the console component ? Of course N…
Now we will see another interesting usage of this component which is download a file given it’s URL.
Let’s get Started :
1. Create a new folder and a new php file
You simply need to create a folder let’s call it StandAloneFileDownload then within create an src folder and finally create a new php file, let’s name it weather.php so we should have this tree : StandAloneFileDownload > src > file-download.php
Then open a Terminal , go to StandAloneFileDownload folder and execute the following command :
composer require symfony/console symfony/http-clientThis will create a vendor folder for you and download two Symfony components needed for our command: symfony/console is for the command line , and symfony/http-client is to download a file.
You should have this tree by now :
2. Create a command taking the file url as argument
Edit your src/file-download.php file and put in this code :
<?php
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Helper\ProgressBar;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\SingleCommandApplication;
use Symfony\Component\HttpClient\HttpClient;
require __DIR__ . '/../vendor/autoload.php';
(new SingleCommandApplication())
->setName('file:download')
->addArgument('url', InputArgument::OPTIONAL, 'File url ', "https://proof.ovh.net/files/100Mb.dat")
->setCode(function (InputInterface $input, OutputInterface $output): int {
// file download code goes here
return Command::SUCCESS;
})
->run();We have defined a single command that takes an optional argument which is the file URL. If no URL is provided we will default to this one : https://proof.ovh.net/files/100Mb.dat a dummy file with 100 MB as size.
2. Download the file using HttpClient Component
Now let’s get the file URL provided in the command’s argument and pass it to our new instance of HttpClient.
We will be downloading the file chunk by chunk and writing their content in the local hard drive with the help of PHP functions fopen and fwrite
That’s what our navigators do behind the scene when downloading a file.
<?php
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Helper\ProgressBar;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\SingleCommandApplication;
use Symfony\Component\HttpClient\HttpClient;
require __DIR__ . '/../vendor/autoload.php';
(new SingleCommandApplication())
->setName('file:download')
->addArgument('url', InputArgument::OPTIONAL, 'File url ', "https://proof.ovh.net/files/10Mb.dat")
->setCode(function (InputInterface $input, OutputInterface $output): int {
$url = $input->getArgument('url');
$httpClient = HttpClient::create();
$response = $httpClient->request('GET', $url);
$filHandler = fopen('./file.dat' , 'w');
foreach ($httpClient->stream($response) as $chunk) {
fwrite($filHandler, $chunk->getContent());
}
return Command::SUCCESS;
})
->run();Now if we execute our command with
$ php file-download.php we will be able to download the file successfully but it’s not too user friendly as no feedback is given to the user while the download is on and the user can get confused quickly:
Let’s improve our command line and show a nice progress bar indicating the progress of the download to keep our user amused.
3. Show a progress bar while file being downloaded
We will create a new Progress bar instance and add another argument to our HttpClient request method: an array containing on_progress callback to be executed in a regular period of time. According to Symfony docs :
This callback is guaranteed to be called on DNS resolution, on arrival of headers and on completion; additionally it is called when new data is uploaded or downloaded and at least once per second
In this call back we are goin to update our progress bar status and finish it once the download is done.
Our final code should look like this :
<?php
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Helper\ProgressBar;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\SingleCommandApplication;
use Symfony\Component\HttpClient\HttpClient;
require __DIR__ . '/../vendor/autoload.php';
(new SingleCommandApplication())
->setName('file:download')
->addArgument('url', InputArgument::OPTIONAL, 'File url ', "https://proof.ovh.net/files/100Mb.dat")
->setCode(function (InputInterface $input, OutputInterface $output): int {
$progressBar = new ProgressBar($output, 100);
$url = $input->getArgument('url');
$httpClient = HttpClient::create();
$response = $httpClient->request('GET', $url, [
'on_progress' => function (int $dlNow, int $dlSize, array $info) use ($progressBar) {
if ($dlSize && $dlNow > 0 ){
$progressBar->setProgress(intval($dlNow*100 / $dlSize));
if ($dlNow == $dlSize){
$progressBar->finish();
}
}
}
]);
$filHandler = fopen('./file.dat' , 'w');
foreach ($httpClient->stream($response) as $chunk) {
fwrite($filHandler, $chunk->getContent());
}
return Command::SUCCESS;
})
->run();The output should look something like this :
And .. Voila ! the user will be staring at the progress bar and wont get bored/confused now.
You can checkout the code in my github repo : https://github.com/zizoujab/StandAloneFileDownload
If you want to see how we can achieve this within a Symfony project you can checkout my this video on my Youtube channel :
Test your knowledge
As a bonus for ending the article here are two questions that will help you memorize some facts seen above:







