Question

I'm trying to copy a remote file (image PNG, GIF, JPG ...) to my server. I use Guzzle since I sometimes get 404 with copy() even if the file exists and I also need to do a basic auth. This script is within a long script launched in command triggered by a cron job. I'm pretty new to Guzzle and I successfully copy the image but my files have wrong mime type. I must be doing something wrong here. Please suggest me a good way to do this (including checking success/failure of copy and mime type check). If file has no mime type I would pop an error with details informations.

Here is the code:

$remoteFilePath = 'http://example.com/path/to/file.jpg';
$localFilePath = '/home/www/path/to/file.jpg';
try {
    $client = new Guzzle\Http\Client();
    $response = $client->send($client->get($remoteFilePath)->setAuth('login', 'password'));
    if ($response->getBody()->isReadable()) {
        if ($response->getStatusCode()==200) {
            // is this the proper way to retrieve mime type?
            //$mime = array_shift(array_values($response->getHeaders()->get('Content-Type')));
            file_put_contents ($localFilePath , $response->getBody()->getStream());
            return true;
        }
    }
} catch (Exception $e) {
    return $e->getMessage();
}

When I do this my mime type is set to application/x-empty

Also it looks like when status is different from 200 Guzzle will automatically throw an exception. How can I stop this behaviour and check status myself so I can custom error message?

EDIT: This was for Guzzle 3.X Now this is how you can do it using Guzzle v 4.X (works as well with Guzzle 6)

$client = new \GuzzleHttp\Client();
$client->get(
    'http://path.to/remote.file',
    [
        'headers' => ['key'=>'value'],
        'query'   => ['param'=>'value'],
        'auth'    => ['username', 'password'],
        'save_to' => '/path/to/local.file',
    ]);

Or using Guzzle stream:

use GuzzleHttp\Stream;

$original = Stream\create(fopen('https://path.to/remote.file', 'r')); 
$local = Stream\create(fopen('/path/to/local.file', 'w')); 
$local->write($original->getContents());

This looks great. Is there better/proper solution when using Guzzle 4?

Was it helpful?

Solution

Your code can be simplified a great deal. My example code below will stream the body of the response directly to the filesystem.

<?php

function copyRemote($fromUrl, $toFile) {
    try {
        $client = new Guzzle\Http\Client();
        $response = $client->get($fromUrl)
            ->setAuth('login', 'password') // in case your resource is under protection
            ->setResponseBody($toFile)
            ->send();
        return true;
    } catch (Exception $e) {
        // Log the error or something
        return false;
    }
}

When I do this my mime type is set to application/x-empty

A filesystem mimetype?

Also it looks like when status is different from 200 Guzzle will automatically throw an exception. How can I stop this behaviour and check status myself so I can custom error message?

Guzzle will throw an exception for bad responses like 4xx and 5xx. No need to disable this. Just catch an exception and deal with the error there.

OTHER TIPS

Look at this with post:

$myFile = fopen('path/to/file', 'w') or die('Problems');
$client = new \Guzzle\Service\Client();
$request = $client->post('https://www.yourdocumentpage.com', array(), ['pagePostField' => 'data'], ['save_to' => $myFile]);
$client->send($request);
fclose($myFile);

here you must send the request of your "post"

and with get

$myFile = fopen('path/to/file', 'w') or die('Problems');
$client = new \GuzzleHttp\Client();
$request = $client->get('https://www.yourdocumentpage.com', ['save_to' => $myFile]);

and here you don't need to send the request, and here you'll find a lot of documentation, you must have guzzle 6 for doing that, and if you are using GOUTTE at the same time you'll need goutte 3.1, update your require in your composer.json

using Guzzle 6 just use SINK option. see below detailed function

Extra:

use GuzzleHttp\Client; Guzzle namespace included

$access_token = if you need auth else simply remove this option

ReportFileDownloadException = custom exception

/**
 * download report file and read data to database
 * @param remote url
 * @return N/A
 * @throws ReportFileDownloadException
 */
protected function getReportFile($report_file_url)
{
    $file = $this->tempDirectory . "/" . basename($report_file_url);
    $fileHandle = fopen($file, "w+");

    try {
        $client = new Client();
        $response = $client->get($report_file_url, [
            RequestOptions::SINK => $fileHandle,
            RequestOptions::HEADERS => [
                "Authorization" => "Bearer $access_token"
            ]
        ]);
    } catch (RequestException $e) {
        throw new ReportFileDownloadException(
            "Can't download report file $report_file_url"
        );
    } finally {
        @fclose($fileHandle);
    }

    throw new ReportFileDownloadException(
        "Can't download report file $report_file_url"
    );
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top