Strange "JPEG datastream contains no image" with PHP - php

I am getting a remote image and checking the size of it.
Sometimes I get a strange error saying JPEG datastream contains no image and I narrowed it down that it's happening at this step, in fact it's happening EXACTLY at imagecreatefromstring. What could be the problem? An issue with the image? Or do I need to increase some kind of memory setting in php.ini or..?
function ranger($url) {
$headers = array(
"Range: bytes=0-32768"
);
$curl = curl_init($url);
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
$data = curl_exec($curl);
curl_close($curl);
return $data;
}
$url = $prod['IMAGE1'];
$raw1 = ranger($url);
$im = imagecreatefromstring($raw1);
$width = imagesx($im);
$height = imagesy($im);

You need to follow these three steps to solve your problem:
As the error message clearly states, you do not have an image. You need to determine why you do not have an image. You could start by running var_dump($raw1) but you could take a look at the algorithm in general. After this step you should know why you do not have an image where you expected one.
?
User a valid image resource as a parameter of imagecreatefromstring.

Related

PHP cURL downloaded file is 0 bytes on an external link while works on another external link

I'm trying to get a file/image downloaded to a folder on my first server, from second server. I have the following code:
$image = 'http://i1.au.reastatic.net/150x112/73fa6c02a92d60a76320d0e89dfbc1a36a6e46c818f74772dec65bae6959c62f/main.jpg';
$imageName = pathinfo( $image, PATHINFO_BASENAME );
$ch = curl_init();
curl_setopt( $ch, CURLOPT_URL, $image );
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
$source = curl_exec( $ch );
curl_close( $ch );
file_put_contents( './content/' . $imageName, $source );
$out = preg_split('/(\r?\n){2}/', $source, 2);
$headers = $out[0];
$headersArray = preg_split('/\r?\n/', $headers);
$headersArray = array_map(function($h) {
return preg_split('/:\s{1,}/', $h, 2);
}, $headersArray);
$tmp = [];
foreach($headersArray as $h) {
$tmp[strtolower($h[0])] = isset($h[1]) ? $h[1] : $h[0];
}
print_r($tmp);
The linked url is not of my servers, but works as expected, writing a file on my first server.
But when I use my own second server link, for example https://example.com/demo1.png, it writes a file of 0 bytes.
On logging out the headers, the external link which is not of my server logs out an array of many items about the image such as "content-type" and "content-length". On logging out the header response of the image of my second server, it logs out an empty array..
What adjustments do I need to do on the script? I'm also okay to do some adjustments on my second server(?), if needed.
Thank you in advance.
Thanks to #RiggsFolly , I found out the issue was related to SSL certificate. The issue seems to be on my local pc server, and may not persist on a live server. I will move to a live server soon, and if the issue persists I will update this answer as needed. I'll mark this answer as best answer, hoping to also let others know that the issue was related with SSL and can be further fixed with proper measures. SO also has many useful questions/answers for the mentioned SSL issue.

Upload large files to Dropbox via HTTP API

I am currently implementing an upload mechanism for files on my webserver into my Dropbox app directory.
As stated on the API docs, there is the /upload endpoint (https://www.dropbox.com/developers/documentation/http/documentation#files-upload) which accepts files up to 150MB in size. However I‘m dealing with images and videos with a potential size of up to 2GB.
Therefore I need to use the upload_session endpoints. There is an endpoint to start the session (https://www.dropbox.com/developers/documentation/http/documentation#files-upload_session-start), to append data and to finish the session.
What currently is unclear to me is how to exactly use these endpoints. Do I have to split my file on my server into 150MB chunks (how would I do that with a video file?) and then upload the first chunk with /start, the next chunks with /append and the last one with /finish? Or can I just specify the file and the API somehow (??) does the splitting for me? Obviously not, but I somehow can‘t get my head around on how I should calculate, split and store the chunks on my webserver and not lose the session inbetween...
Any advice or further leading links are greatly appreciated. Thank you!
As Greg mentioned in the comments, you decide how to manage the "chunks" of the files. In addition to his .NET example, Dropbox has a good upload session implementation in the JavaScript upload example of the Dropbox API v2 JavaScript SDK.
At a high-level, you're splitting up the file into smaller sizes (aka "chunks") and passing those to the upload_session mechanism in a specific order. The upload mechanism has a few parts that need to be used in the following order:
Call /files/upload_session/start. Use the resulting session_id as a parameter in the following methods so Dropbox knows which session you're interacting with.
Incrementally pass each "chunk" of the file to /files/upload_session/append_v2. A couple things to be aware of:
The first call will return a cursor, which is used to iterate over the file's chunks in a specific order. It gets passed as a parameter in each consecutive call to this method (with the cursor being updated on every response).
The final call must include the property "close": true, which closes the session so it can be uploaded.
Pass the final cursor (and commit info) to /files/upload_session/finish. If you see the new file metadata in the response, then you did it!!
If you're uploading many files instead of large ones, then the /files/upload_session/finish_batch and /files/upload_session/finish_batch/check are the way to go.
I know this is an old post, but here is a fully functional solution for your problem. Maybe anyone else finds it usefull. :)
<?php
$backup_folder = glob('/var/www/test_folder/*.{sql,gz,rar,zip}', GLOB_BRACE); // Accepted file types (sql,gz,rar,zip)
$token = '<ACCESS TOKEN>'; // Dropbox Access Token;
$append_url = 'https://content.dropboxapi.com/2/files/upload_session/append_v2';
$start_url = 'https://content.dropboxapi.com/2/files/upload_session/start';
$finish_url = 'https://content.dropboxapi.com/2/files/upload_session/finish';
if (!empty($backup_folder)) {
foreach ($backup_folder as $single_folder_file) {
$file_name= basename($single_folder_file); // File name
$destination_folder = 'destination_folder'; // Dropbox destination folder
$info_array = array();
$info_array["close"] = false;
$headers = array(
'Authorization: Bearer ' . $token,
'Content-Type: application/octet-stream',
'Dropbox-API-Arg: '.json_encode($info_array)
);
$chunk_size = 50000000; // 50mb
$fp = fopen($single_folder_file, 'rb');
$fileSize = filesize($single_folder_file); // File size
$tosend = $fileSize;
$first = $tosend > $chunk_size ? $chunk_size : $tosend;
$ch = curl_init($start_url);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, fread($fp, $first));
curl_setopt($ch,CURLOPT_RETURNTRANSFER,true);
$response = curl_exec($ch);
$tosend -= $first;
$resp = explode('"',$response);
$sesion = $resp[3];
$position = $first;
$info_array["cursor"] = array();
$info_array["cursor"]["session_id"] = $sesion;
while ($tosend > $chunk_size)
{
$info_array["cursor"]["offset"] = $position;
$headers[2] = 'Dropbox-API-Arg: '.json_encode($info_array);
curl_setopt($ch, CURLOPT_URL, $append_url);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_POSTFIELDS, fread($fp, $chunk_size));
curl_exec($ch);
$tosend -= $chunk_size;
$position += $chunk_size;
}
unset($info_array["close"]);
$info_array["cursor"]["offset"] = $position;
$info_array["commit"] = array();
$info_array["commit"]["path"] = '/'. $destination_folder . '/' . $file_name;
$info_array["commit"]["mode"] = array();
$info_array["commit"]["mode"][".tag"] = "overwrite";
$info_array["commit"]["autorename"] = true;
$info_array["commit"]["mute"] = false;
$info_array["commit"]["strict_conflict"] = false;
$headers[2] = 'Dropbox-API-Arg: '. json_encode($info_array);
curl_setopt($ch, CURLOPT_URL, $finish_url);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_POSTFIELDS, $tosend > 0 ? fread($fp, $tosend) : null);
curl_exec($ch);
curl_close($ch);
fclose($fp);
unlink($single_folder_file); // Remove files from server folder
}
}

Get Image With file_get_contents it return Not Found Error

I have one Image on another server (Image).but when i get this image With file_get_contents() function it will return
Not Found Error
and generate this Image.
file_put_contents(destination_path, file_get_contents(another_server_path));
plz help me. if there are another way to get those image.
Try this.
There is problem with URL Special character.then you have to decode some special character from url basename.
$imgfile = 'http://www.lagrolla.com.au/image/m fr 137 group.jpg';
$destinationPath = '/path/to/folder/';
$filename = basename($imgpath);
$imgpath = str_replace($filename,'',$imgpath).rawurldecode($filename);
copy($imgfile,$destination_path.$filename);
Another way to download copy file from another server is using curl:
$ch = curl_init('http://www.lagrolla.com.au/image/data/m%20fr%20137%20group.jpg');
$destinationPath = '/path/to/folder/filenameWithNoSpaces.jpg';
$fp = fopen($destinationPath, 'wb');
curl_setopt($ch, CURLOPT_FILE, $fp);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_exec($ch);
curl_close($ch);
fclose($fp);
Note: It is bad practice to save images with spaces in file name, so you should save this file with proper name.

How to display binary data from curl in php

I'm writing simple php proxy and I have trouble displaying png file, the output is
and it should be:
The images are opened in Notepad++. My php curl code look like this:
$ua = 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.13) Gecko/20080311 Firefox/2.0.0.13';
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_USERAGENT, $ua);
curl_setopt($ch, CURLOPT_HEADER_OUT, 1);
curl_setopt($ch, CURLOPT_BINARYTRANSFER, 1);
$content = curl_exec($ch);
$info = curl_getinfo($ch);
header('Content-Type:' . $info['content_type']);
echo $content
I try with and without CURLOPT_BINARYTRANSFER the output is the same and the image is not displaying. How can I display the image?
EDIT: when I'm saving the data to the file and redirect using Location header the image is displayed correctly:
$file = fopen('proxy_tmp~', 'w');
fwrite($file, $content);
fclose($file);
header('Location: ' . DIR . 'proxy_tmp~');
EDIT 2: I had gzip compression, bu when I disabled it I have the same issue, when I open both files in Notepad++ one is DOS/Windows ANSI (original) and the other is DOS/Windows UTF-8 (the file opened by a script). When I open a file in Notepad and change encoding to ANSI and save the file, everything is ok.
EDIT 3: I think I did the same thing on GNU/Linux but without CURLOPT_BINARYTRANSFER option and it's working fine, here is my project https://github.com/jcubic/yapp. I've also test it on Windows 10 with Wamp and also work fine.
Here is how to send the file directly back to the user for download (uses your $content var):
$file_array = explode("\n\r", $content, 2);
$header_array = explode("\n", $file_array[0]);
foreach($header_array as $header_value) {
$header_pieces = explode(':', $header_value);
if(count($header_pieces) == 2) {
$headers[$header_pieces[0]] = trim($header_pieces[1]);
}
}
header('Content-type: ' . $headers['Content-Type']);
header('Content-Disposition: ' . $headers['Content-Disposition']);
echo substr($file_array[1], 1);
There is a full example here:
http://ryansechrest.com/2012/07/send-and-receive-binary-files-using-php-and-curl/
never use the 'w' mode. it means the "text mode", because the default is, unfortunately, text mode.
on Windows, "text mode" means: whenever you try to write an \n byte (ascii 10, newline) and it's not preceded by an \r byte (ascii 13, carriage-return), insert an \r byte before writing the \n byte. (it also means text mode on linux/modern MacOS, but on Linux/modern MacOS, the text mode does absolutely nothing and is treated the same way as binary mode. * not true for classic MacOS <=9 from <=2001, on Classic MacOS, text mode did weird shit like it does weird shit on Windows)
always use the binary mode, eg wb, to make your program portable across both Windows and Linux
(actually modern versions of PHP automatically convert it to binary mode when not specified, but this question was asked in 2013, and this is still a very important rule-of-thumb if you ever use any other language than PHP. if you actually want text mode, explicitly enable text mode with wt, but you pretty much never want text mode, it's a horrible thing, made worse by the fact that it's the default mode for the underlying fopen() C api.. it's so horrible in fact, that Linux never supported it at all, and Apple/MacOS dropped support in 2001 with the release of MacOS X.. AFAIK Windows is the last major OS to actually support it, somethingsomething backwards-compatibility )
Which php version are you using?
Are you above PHP version 5.1.3? Then the CURLOPT_BINARYTRANSFER will have no effect.
Source:
http://php.net/manual/en/function.curl-setopt.php
Looking at the file, you should add the following header to you're page:
header('Content-Type: image/png');
I have used following with basic header authentication.. used postman to get auth key
// key : > converted to basic auth
// Content type
header('Content-Type: image/jpeg');
$url = 'http://com.com.com/api/images/products/264/7971';
$headers = array(
'Content-Type: image/png',
'Authorization: Basic ##$%##$%##$%##$%##$%##$%' );
$ch = curl_init();
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_URL,$url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
//curl_setopt($ch, CURLOPT_USERPWD, "$login:$password");
$file = curl_exec($ch);
curl_close($ch);
//echo($result);
// create image from output of the screen
$img = #imagecreatefromstring($file);
if ($img === false) {
echo $file;
exit;
}
$width = imagesx($img);
$height = imagesy($img);
$newWidth = 300;
$ratio = $newWidth / $width;
$newHeight = $ratio * $height;
$resized = #imagecreatetruecolor($newWidth, $newHeight);
if ($resized === false) {
echo $file;
exit;
}
$quality = 90;
if (#imagecopyresampled($resized, $img, 0, 0, 0, 0, $newWidth, $newHeight, $width, $height)) {
#imagejpeg($resized, NULL, $quality);
} else {
echo $file;
}
Use $content_arr = explode("\r\n\r\n",$content,2); to divide the content into headers and body, set the image/png header and then do echo trim($content_arr[1]); to get rid of spaces or blank lines that prevent the browser from being able to read the image.
In my case adding Content-Length header fixed problems with binary files proxy.
I have this issue, even if I read png content from disk (file_get_contents function). One php source file's encoding was UTF-8 with signature and that was the source of "my" problem.
So I remove the signature with Notepad2. There is option to change file encoding.

how to check if a url is an image url with php?

I need to check the url is image url or not? How can i do this?
Examples :
http://www.google.com/ is not an image url.
http://www.hoax-slayer.com/images/worlds-strongest-dog.jpg is an image url.
https://stackoverflow.com/search?q=.jpg is not an image url.
http://www.google.com/profiles/c/photos/private/AIbEiAIAAABECK386sLjh92M4AEiC3ZjYXJkX3Bob3RvKigyOTEzMmFmMDI5ODQ3MzQxNWQxY2VlYjYwYmE2ZTA4YzFhNDhlMjBmMAEFQ7chSa4PMFM0qw02kilNVE1Hpw is an image url.
If you want to be absolutely sure, and your PHP is enabled for remote connections, you can just use
getimagesize('url');
If it returns an array, it is an image type recognized by PHP, even if the image extension is not in the url (per your second link). You have to keep in mind that this method will make a remote connection for each request, so perhaps cache urls that you already probed in a database to lower connections.
You can send a HEAD request to the server and then check the Content-type. This way you at least know what the server "thinks" what the type is.
You can check if a url is an image by using the getimagesize function like below.
function validImage($file) {
$size = getimagesize($file);
return (strtolower(substr($size['mime'], 0, 5)) == 'image' ? true : false);
}
$image = validImage('http://www.example.com/image.jpg');
echo 'this image ' . ($image ? ' is' : ' is not') . ' an image file.';
i think that the idea is to get a content of the header url via curl
and check the headers
After calling curl_exec() to get a web page, call curl_getinfo() to get the content type string from the HTTP header
look how to do it in this link :
http://nadeausoftware.com/articles/2007/06/php_tip_how_get_web_page_content_type#IfyouareusingCURL
Can use this:
$is = #getimagesize ($link);
if ( !$is ) $link='';
elseif ( !in_array($is[2], array(1,2,3)) ) $link='';
elseif ( ($is['bits']>=8) ) $srcs[] = $link;
Here is a way that requires curl, but is faster than getimagesize, as it does not download the whole image.
Disclaimer: it checks the headers, and they are not always correct.
function is_url_image($url){
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url );
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HEADER, 1);
curl_setopt($ch, CURLOPT_NOBODY, 1);
$output = curl_exec($ch);
curl_close($ch);
$headers = array();
foreach(explode("\n",$output) as $line){
$parts = explode(':' ,$line);
if(count($parts) == 2){
$headers[trim($parts[0])] = trim($parts[1]);
}
}
return isset($headers["Content-Type"]) && strpos($headers['Content-Type'], 'image/') === 0;
}
$ext = strtolower(end(explode('.', $filename)));
switch($ext)
{
case 'jpg':
///Blah
break;
}
Hard version (just trying)
//Turn off E_NOTICE reporting first
if(getimagesize($url) !== false)
{
//Image
}

Categories