Imagick from Azure Blob - php

I have a PHP site that currently pulls images off an Azure Blob, writes it to disk using file_put_contents, then imagick reads the file from the disk using readImageFile. I would rather this live in memory than be wrote to disk, then read from disk. How can I accomplish this? When I try to ReadImageBlob, I get the below error:
Warning: Imagick::readimageblob() expects parameter 1 to be string, resource given in <file> <line>
Below is a snippet of my code (This is just testing code, not production):
// Get Data from Azure Storage Blob
$blob = $blobClient->getBlob($containerName, $documentPath);
// Get TIF file from Blob and convert to PDF
$im = new imagick();
$im->readImageBlob($blob->getContentStream());
$im->setImageFormat('pdf');
// Echo as PDF
header('Content-Type: application/pdf');
echo $im;

You should be able to use stream_get_contents to read the string from the stream that you have. Example:
$im->readImageBlob(stream_get_contents($blob->getContentStream()));

Related

PHP - Possible to take a base64 encoded pdf data string and compress it?

So I have an XML file that has a base64 encoded data string for a pdf file, which just has an image taken from an iPad.
This pdf file can be excessively large, as much as 14MB with dimensions of 57"x38".
These images are taken from an iPad through a DocuSign session, thus I have no way at the moment of controlling their size or format before they get to my php listener script.
However, my script cannot work with such large files as my CRM's API file size max is 10MB, and I need a way of reducing the file size before I can upload it through my CRM's API.
Now if it was just a jpg, it would be ok as there are plenty of ways to reduce file size in PHP, but it is a PDF. I have found plenty of PHP extensions for making PDFs, but I haven't found any for reading a PDF and extracting an image from it.
So is there a way to extract the image from the PDF through PHP, or perhaps compress the pdf file?
UPDATE
I didn't think about the possibility of converting a pdf into a jpg, which apparently is easier to do with imagick. Having my server admin install it and I will see if I can make it work with my script.
UPDATE 2
So I was able to get imagick working and locally I am able to convert pdf files into jpg, and reduce file size dramatically.
However, I am running into an issue using it with my application. I get the following error from my CRM's API:
Failed to parse XML-RPC request: Invalid byte 1 of 1-byte UTF-8 sequence.
So the process is the following:
XML file has a base64 encoded data stream of the pdf file.
I decode this data
I then convert with imagick and reduce file size
I base64 encode and prep for upload
CODE
$imageBlob = base64_decode((string)$pdf->PDFBytes);
$imagick.$x = new Imagick();
$imagick.$x->readImageBlob($imageBlob);
$imagick.$x->setImageFormat('jpeg');
$imagick.$x->setImageCompressionQuality(60);
$imagick.$x->adaptiveResizeImage(1024,768,true);
$imageBlob = $imagick.$x->getImageBlob();
$PDFdata[] = base64_encode($imageBlob);
I can test the date by using the proper header and I can see the new jpeg fine, so I assume the data is properly formatted.
What I am missing?
Ok, so I figured it out.
Imagick was the way to go, and my use of it was good. I just goofed up on the file name because I wasn't using a proper dynamic variable name. Code should have looked like this:
CODE
$imageBlob = base64_decode((string)$pdf->PDFBytes);
${'imagick'.$x} = new Imagick();
${'imagick'.$x}->readImageBlob($imageBlob);
${'imagick'.$x}->setImageFormat('jpeg');
${'imagick'.$x}->setImageCompressionQuality(60);
${'imagick'.$x}->adaptiveResizeImage(1024,768,true);
$imageBlob = ${'imagick'.$x}->getImageBlob();
$PDFdata[] = base64_encode($imageBlob);
$PDFfile[] = $FormCustomField . $x . '.jpg';
So the error I was getting was because of an invalid file name, because the $x variable in the previous code was getting junk values. Now everything works fine.

php imagick won't save PNG compressed but shows compressed in browser

I have the following code in PHP to take the screenshot of first page of the PDF.
$name = getcwd()."\\testfile";
$img = new imagick();
$img->setResolution(200,200);
$img->readImage($name.'.pdf[0]');
$img->setImageResolution(100,100);
$img->resampleImage(100,100,imagick::FILTER_LANCZOS,1);
$img->setImageCompression(\Imagick::COMPRESSION_ZIP );
$img->setImageCompressionQuality('0');
$img->setImageFormat('png8');
$img->writeImage($name.".png");
header("Content-type : image/png");
echo $img;
This code produces the PNG of 62kb only in the Google Chrome's Resource monitor tab. But the image which is written by Imagick() is above 114kb. Just to make sure image isn't compressed and or any other issues i have used a online service called TinyPNG and they compressed the image shrinking it to exactly 62kb i get in browser...
What could be wrong in this code? Also i am using PNG8 format because thats more efficient.
Best
Ahsan
I think this is caused by your writeImage statement. If you write a PNG image without specifying png8: specifically in the filename your image will not be stored in that format. In essence setImageFormat will only affect when you retrieve the image as a string (echo $img).
If you do the following:
$img->writeImage ('png8:' . $name . ".png");
it should be stored as a png8. You can verify this with identify -verbose and checking the Depth / Channel Depth.
These are the default compression methods used for the following common image formats:
PNG: Imagick::COMPRESSION_ZIP
JPEG: Imagick::COMPRESSION_JPEG
GIF: Imagick::COMPRESSION_LZW

stream audio/video files from gridFS on the browser

I have been trying to read an audio file from mongoDB which i have stored using GridFS. I could download the file in the system and play from it but I wanted to stream those audio/video files from the DB itself and play it in the browser. Is there anyway to do that without downloading the file to the system? Any help would be good.
The PHP GridFS support has a MongoGridFSFile::getResource() function that allows you to get the stream as a resource - which doesn't load the whole file in memory. Combined with fread/echo or stream_copy_to_stream you can prevent the whole file from being loaded in memory. With stream_copy_to_stream, you can simply copy the GridFSFile stream's resource to the STDOUT stream:
<?php
$m = new MongoClient;
$images = $m->my_db->getGridFS('images');
$image = $images->findOne('mongo.png');
header('Content-type: image/png;');
$stream = $image->getResource();
stream_copy_to_stream( $stream, STDOUT );
?>
Alternatively, you can use fseek() on the returned $stream resource to only send back parts of the stream to the client. Combined with HTTP Range requests, you can do this pretty efficiently.
If the other recipe fails, for example with NginX and php-fpm, because STDOUT is not available in fpm, you can use
fpassthru($stream);
instead of
stream_copy_to_stream( $stream, STDOUT );
So a complete solution looks like:
function img($nr)
{
$mongo = new MongoClient();
$img = $mongo->ai->getGridFS('img')->findOne(array('metadata.nr'=>$nr));
if (!$img)
err("not found");
header('X-Accel-Buffering: no');
header("Content-type: ".$img->file["contentType"]);
header("Content-length: ".$img->getSize());
fpassthru($img->getResource());
exit(0);
}
FYI:
In this example:
File is not acccessed by the filename, instead it is accessed by a number stored in the metadata. Hint: You can set an unique index to ensure, that no number can be used twice.
Content-Type is read from GridFS, too, so you do not need to hardcode this.
NginX caching is switched off to enable streaming.
This way you can even handle other things like video or html pages. If you want to enable NginX caching, perhaps only output X-Accel-Buffering on bigger sizes.

Uploading PNG file to nginx server via HTTP_RAW_POST_DATA results in corrupted image

I'm trying to debug this issue by posting raw PNG image data to the server with the help of Postman. Here's a screenshot, which might help to understand the issue:
On the server I'm receiving the file as follows:
$png = $GLOBALS["HTTP_RAW_POST_DATA"];
Then I write the data to a new file:
$fh = fopen($myFile, 'w') or die("can't open file");
fwrite($fh, $png);
fclose($fh);
The file gets saved correctly, but it now has a different file size,
417KB instead of 279KB which is the size of the original file.
Now of course, I can't do any image operation as none of the functions (such as getimagesize which returns bool(false)) recognizes the file as a valid image.
I have debugged this process to a point where the issue must be somewhere in the file operations, but I don't understand why the file just doesn't result in the very same file type and size as the original, when the only thing I am doing is using the same raw data.
UPDATE:
I've now compared the encodings of the original file with the uploaded one,
and the former is in ISO-8859-1 and it displays correctly, the latter is in UTF-8 and has about 138kB more in file size.
Now I've achieved to convert the file on the server to ISO-8859-1.
fwrite($fh, iconv("UTF-8", "ISO-8859-1", $png));
The resulting file does now have the same output file size (279kB),
but it is still not recognized as a PNG image, some information seems to still get lost.
UPDATE (1):
I've been able to examine the issue further and found out, that the original file is exactly 4 bytes bigger than the generated file, thus the resulting PNG seems to be corrupted.
UPDATE (2):
I'm now able to save the file and open it as a valid PNG. The following code seems to be saving the image correctly:
$input = fopen("php://input","r+");
$destination = fopen($myFile, 'w+');
stream_copy_to_stream($input, $destination);
fclose($input);
fclose($destination);
However when trying to open the file with the imagecreatefrompng function I get a 500 error. I'm now trying to figure out if it's a memory issue in PHP.
Problem might be the way you test your POST by copying the "binary" data into a text field.
If you paste the same data into a text editor you won't get a valid image file either when saving this with the png extension.
Try to build a simple form with file field to test your upload
I use nginx for uploads and haven't had a problem, but I use the standard PHP way of uploading files as per: http://www.php.net/manual/en/features.file-upload.post-method.php
I would suggest trying that.
Try using: < ?php $postdata = file_get_contents("php://input"); ?>
To get the raw data. I use it some times to get data sent from a ajax post on cake.

Resize image directly from Rackspace Cloud Files 'object' without downloading?

I have moved my images to Rackspace Cloud Files and am using their PHP API. I am trying to do the following:
Get an image from my "originals" container
Resize it, sharpen it, etc.
Save the resized image to the "thumbs" container
My problem is with #2. I was hoping to resize without having to copy the original to my server first (since the images are large and I'd like to resize dynamically), but can't figure out how. This is what I have so far (not much):
$container = $conn->get_container("originals");
$obj = $container->get_object("example.jpg");
$img = $obj->read();
Part of the problem is I don't fully understand what is being returned by the read() function. I know $img contains the object's "data" (which I was able to print out as gibberish), but it is neither a file nor a url nor an image resource, so I don't know how to deal with it. Is it possible to convert $img into an image resource somehow? I tried imagecreatefromjpeg($img) but that didn't work.
Thanks!
First, you cannot resize an image without loading it into memory. Unless the remote server offers some "resize my image for me, here are the parameters" API, you have to load the image in your script to manipulate it. So you'll have to copy the file from the CloudFiles container to your server, manipulate it, then send it back into storage.
The data you receive from $obj->read() is the image data. That is the file. It doesn't have a file name and it's not saved on the hard disk, but it is the entire file. To load this into gd to manipulate it, you can use imagecreatefromstring. That's analogous to using, for example, imagecreatefrompng, only that imagecreatefrompng wants to read a file from the file system by itself, while imagecreatefromstring just accepts the data that you have already loaded into memory.
You can try to dump the content of the $img variable into a writable file as per the below:
<?php
$filename = 'modifiedImage.jpg';
/*
* 'w+' Open for reading and writing; place the file pointer at the beginning of the file and truncate
* the file to zero length. If the file does not exist, attempt to create it.
*/
$handle = fopen($filename, 'w+');
// Write $img to the opened\created file.
if (fwrite($handle, $img) === FALSE) {
echo "Cannot write to file ($filename)";
exit;
}
echo "Success, wrote to file ($filename)";
fclose($handle);
?>
More details:
http://www.php.net/manual/en/function.fopen.php
http://www.php.net/manual/en/function.fwrite.php
Edit:
You might also want to double check the type of data returned by the read() function, because if the data is not a jpg image, if it's for example a png, the extension of the file needs to be changed accordingly.

Categories