1

I am downloading a recording from an external url and saving it using CURL as follows:

$ch = curl_init($Recording); 
$fp = fopen($recording_file_loc, 'wb'); 
curl_setopt($ch, CURLOPT_FILE, $fp); 
curl_setopt($ch, CURLOPT_HEADER, 0); 
curl_exec($ch);  
curl_close($ch); 
fclose($fp); 

I need to change the file permissions once file is completely downloaded as follows.

chmod($recording_file_loc , 0640);

How can i check and ensure that file is completely downloaded before executing chmod??

updated: I updated my code as follows:

$statusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);

and

if($statusCode == 200){
chmod($recording_file_loc , 0640);
}
else{
echo $statusCode;
}
Pamela
  • 684
  • 1
  • 7
  • 21
  • have you tried `if (fclose($fp) ) chmod..` -- because fclose returns true when finishes. and php is synchronous language and I think will wait for curl if fire is created – Kresimir Pendic Jul 27 '20 at 07:00
  • You could check if curl reported an error - https://stackoverflow.com/questions/3987006/how-to-catch-curl-errors-in-php – Nigel Ren Jul 27 '20 at 07:02

3 Answers3

1

You need to put to check if the download process is complete.

curl_setopt($ch, CURLOPT_PROGRESSFUNCTION, 'progress'); // call progress function
curl_setopt($ch, CURLOPT_NOPROGRESS, false);

Then you need to define a function which checks the download progress

// progress function definition
function progress($resource,$download_size, $downloaded, $upload_size, $uploaded)
{
    // Progress
    if($download_size > 0)
         echo $downloaded / $download_size  * 100;

    if($downloaded / $download_size == 1){

        // chmod code here
    }

}

Check this link cURL download progress in PHP

Chilarai
  • 1,842
  • 2
  • 15
  • 33
  • 1
    I think it is better to check http status codes as follows: $statusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); and do : if($statusCode == 200){ chmod($recording_file_loc , 0640); } else{ echo "Status Code: " . $statusCode; } – Pamela Jul 27 '20 at 07:27
  • 1
    Yes thats possible. True – Chilarai Jul 27 '20 at 07:28
  • insufficient. if a page responds `HTTP 200 OK\r\nContent-Length: 3` but then only sends 2 bytes of the body and close the connection, it will still be 200 OK, but your download will be incomplete. – hanshenrik Jul 27 '20 at 08:07
1

on transfers where curl detected any errors, curl_errno($ch) should no longer return 0, so if(curl_errno($ch)!==0), something bad probably happened to your download.

another thing, as pointed out by @Pamela in a comment, if the response code is not 2XX (like HTTP 200 OK or HTTP 204 No Content), that's another sign something probably went wrong, which can be detected by doing if(((string)curl_getinfo($ch,CURLINFO_RESPONSE_CODE))[0]!=='2')

so..

if(curl_errno($ch)!==0 || ((string)curl_getinfo($ch,CURLINFO_RESPONSE_CODE))[0]!=='2'){
    // the download probably failed. 
}

generally speaking, this may be impossible to detect on servers that doesn't implement "Content-Length" headers, if you're downloading from a server that doesn't support Content-Length, then there may be no standardized way to detect the broken download at all.. (you may have to inspect what you've downloaded to make sure it's what you expect or something, idk)

for example, on transfers where the body length doesn't match the "Content-Length" header, curl_errno($ch) returns int(56) (instead of the usual int(0)), and curl_exec($ch) returns bool(false) (PS! if you used CURLOPT_RETURNTRANSFER, then it may contain a string instead of bool)

here's a little HTTP server sending "Content-Length: 3", then cutting the connection after just sending 2 (of allegedly 3) bytes of the body:

<?php

$port=1234;
$srv=socket_create_listen($port);
while(($conn=socket_accept($srv))){
        $headers=implode("\r\n",array(
                "HTTP/1.1 200 OK",
                "Content-Type: text/plain",
                "Content-Length: 3",
                "Connection: close",
                "","",
        ));
        // i lied! i said 3 bytes body, but only send 2 bytes body
        $body="ab";
        $response=$headers.$body;
        var_dump(strlen($response),socket_write($conn,$response));
        socket_close($conn);
}

and an accompanying test script:

<?php

$ch=curl_init("http://127.0.0.1:1234");
var_dump(curl_exec($ch));
var_dump(curl_errno($ch),curl_error($ch));

printing:

abbool(false)
int(56)
string(38) "Recv failure: Connection reset by peer"
hanshenrik
  • 19,904
  • 4
  • 43
  • 89
0

Get the info like this:

$ch = curl_init($Recording); 
$fp = fopen($recording_file_loc, 'wb'); 
curl_setopt($ch, CURLOPT_FILE, $fp); 
curl_setopt($ch, CURLOPT_HEADER, 0); 
curl_exec($ch);
$info = curl_getinfo($ch);
curl_close($ch); 
fclose($fp);

Then check the "download_content_length" against "size_download" like this:

if($info["download_content_length"]==$info["size_download"])
{
    //Download complete!
}
else
{
    //Error
}

Note that it works only if server sends the Content-Length header in advance.

Farhad Malekpour
  • 1,314
  • 13
  • 16