Why can't I get by from .png image to base64-encoded, back to .png image w / PHP?
What I am trying to do is simple and should be very simple in my head. The results were less than encouraging. What I am trying to do: I have a web page that receives base64 encoded PNG image data. I can and successfully took this data with the correct steps to create an image on the filesystem. Call it "Script A":
$imageData = $_POST['png'];
$imageStr = base64_decode($imageData);
$image = imagecreatefromstring($imageStr);
imagepng($image, $somePath);
This works great. But now I have a new requirement that should then read this image, base64-encode it, and send it to Script A, which in turn will return it back to the filesystem as above. Call this second script that reads the data and returns it back to Script A, "Script B". This is when Script A tries to call imagepng on data received from Script B that I get my error:
imagepng(): supplied argument is not a valid Image resource
Still with me? Impressive. So what this all means to me is that there is something wrong with the way Script B reads .png data from the filesystem, encodes it, and sends it to Script A. It does it like this (w / o error handling here is in the interest of brevity, but no errors occur) -
// Read in image file, base64-encode
$fd = fopen($somePath, 'rb');
$size = filesize($somePath);
$data = fread($fd, $size);
fclose($fd);
$encoded = base64_encode($data);
It is then sent to Script A using cURL, w / "$ encoded" as a parameter.
Now I have found many examples of this methodology on the Net. I'm pretty sure this will work. This is not true. I've also tried using file_get_contents. Also tried using "imagecreatefrompng" to read the image, then using the output buffer control to capture the image as a stream and base64 encoding which ... no luck. In the end, I think the above approach is the simplest and most common.
Sorry if this post is too long and / or too harsh to follow. Lots of moving parts - everything. Any feedback would be greatly appreciated.
Greetings
UPDATE: Per Jon Skeet's excellent suggestion to examine the image string, when I go through the code with MD5, I narrowed down the problem - as soon as I write the image to the filesystem in Script B and then read it back into, it changed. So:
$imageStr = base64_decode($imageData);
$image = imagecreatefromstring($imageStr);
imagepng($image, $somePath);
... seems to be manipulating data because when I read it back into w / plain old fread () the MD5 signature has changed. I think this is imagepng function, maybe it adds metadata? It's not clear in php docs. If so, I think instead of reading the image into w / fread (), I would need to do the opposite of what I am doing when I write it, for example:
$image = imagecreatefrompng($somePath);
... then figure out a way to get the image content directly. I can't figure out how to do this: although it requires the "imagepng ($ image)" function, w / oa path ... and what I don't want to do to output the image content.
At this point I think the question is:
- how can I undo (or vice versa) what imagepng seems to be doing, or
- how can I directly output the content of an image resource.
source to share
I suggest you experiment by taking the MD5 hash of the data in the filesystem that Script B reads, again after reading, and again in Script A after decoding it. This should show you which point is changing the data.
Just like the thought is the data certainly valid on the filesystem that Script B reads?
EDIT: Ok, since as per your edit, it looks like the usage imagepng
changes the content. Do you really need to use it imagepng
at all? Are you really doing something with the image? It looks like you are just trying to send a file from Script B to Script A ... so I would do it without accessing the file as anything other than opaque binary data.
source to share
You might want to double check that GD PNG support is indeed enabled in the Script B system.
He should be on this day and aged, but ...
if (imagetypes() & IMG_PNG) {
echo "PNG Support is enabled";
}
PS I'm surprised you just didn't use the HTML / PHP built-in file upload mechanism .
cURL can upload files via:
$ch = curl_init();
// file is the field name, file names are prefixed with @
// In this example, $_FILES['file'] on the remote script would have this
// file metadata
$data = array('file' => '@/home/user/test.png');
curl_setopt($ch, CURLOPT_URL, 'http://localhost/upload.php');
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
curl_exec($ch);
source to share
Zero experience with PHP and cURL, but is it possible for you (by accident) to pass image data as a GET parameter instead of POST (this is the first mistake I would make anyway?)
I think the easiest way to track down the error is to make sure the correct base64 encoding is a) generated by Script B and b) produced by Script A by printing these values ββand comparing them. This should tell you where your problem is.
source to share