I am using PHP CURL to generate a customized PNG image from a REST API. Once this image has loaded I would like to upload it into an AWS S3 Bucket and show the link to it.
Here's my script so far:
$ch = curl_init();
$timeout = 5;
curl_setopt($ch, CURLOPT_URL, 'http://url-to-generate-image.com?options=' + $_GET['options']);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$data = curl_exec($ch);
$info=curl_getinfo($ch);
curl_close($ch);
//require S3 Class
if (!class_exists('S3')) {
require_once('S3.php');
}
//AWS access info
if (!defined('awsAccessKey')) {
define('awsAccessKey', 'MY_ACCESS_KEY');
}
if (!defined('awsSecretKey')) {
define('awsSecretKey', 'MY_SECRET_KEY');
}
//instantiate the class
$s3 = new S3(awsAccessKey, awsSecretKey);
$s3->putBucket('bucket-name', S3::ACL_PUBLIC_READ);
$file_name = md5(rand(99,99999999)) + '-myImage.png';
if ($s3->putObjectFile($data, 'bucket-name' , $file_name, S3::ACL_PUBLIC_READ)) {
echo 'success';
$gif_url = 'http://bucket-name.s3.amazonaws.com/'.$file_name;
} else {
echo 'failed';
}
It keeps failing. Now, I think the problem is with where I use putObjectFile - the $data variable represents the image, but maybe it has to be passed in another way?
I am using a common PHP Class for S3: http://undesigned.org.za/2007/10/22/amazon-s3-php-class
Use PHP memory wrapper to store the contents of the image, and use $s3->putObject() method:
$fp = fopen('php://memory', 'wb');
fwrite($fp, $data);
rewind($fp);
$s3->putObject([
'Bucket' => $bucketName,
'Key' => $fileName,
'ContentType' => 'image/png',
'Body' => $fp,
]);
fclose($fp);
Proven method (you may need to alter the code a bit) with PHP 5.5 and latest AWS libraries.
http://php.net/manual/en/wrappers.php.php
Related
I have a Laravel web app in which users can upload files. These files can be sensitive and although they are stored on S3 they are only accessed via my webservers (streamed download). Once uploaded users may wish to download a selection of these files.
Previously when users went to download a selection of files my web server would download the files from S3, zip them locally and then send the zip down to the client. However once in production due to file sizes the server response would frequently time out.
As an alternative method I want to zip the files on the fly via ZipStream but I haven't had much luck. The zip file either ends up with corrupted files or is corrupted itself and incredibly small.
If it possible to pass a stream resource for a file on S3 to ZipStream and what is the best way to address my timeout issues?
I have tried several method my most recent two are as follows:
// First method using fopen
// Results in tiny corrupt zip files
if (!($fp = fopen("s3://{$bucket}/{$key}", 'r')))
{
die('Could not open stream for reading');
}
$zip->addFileFromPath($file->orginal_filename, "s3://{$bucket}/{$key}");
fclose($fp);
// Second method tried get download the file from s3 before sipping
// Results in a reasonable sized zip file that is corrupt
$contents = file_get_contents("s3://{$bucket}/{$key}");
$zip->addFile($file->orginal_filename, $contents);
Each of these sits within a loop that goes through each files. After the loop I call $zip->finish().
Note I do not get any php errors just corrupt files.
In the end the solution was to use signed S3 url's and curl to provide a file stream for ZipStream as demonstrated by s3 bucket steam zip php. The resulting code edited from the aforementioned source is as follows:
public function downloadZip()
{
// ...
$s3 = Storage::disk('s3');
$client = $s3->getDriver()->getAdapter()->getClient();
$client->registerStreamWrapper();
$expiry = "+10 minutes";
// Create a new zipstream object
$zip = new ZipStream($zipName . '.zip');
foreach($files as $file)
{
$filename = $file->original_filename;
// We need to use a command to get a request for the S3 object
// and then we can get the presigned URL.
$command = $client->getCommand('GetObject', [
'Bucket' => config('filesystems.disks.s3.bucket'),
'Key' => $file->path()
]);
$signedUrl = $request = $client->createPresignedRequest($command, $expiry)->getUri();
// We want to fetch the file to a file pointer so we create it here
// and create a curl request and store the response into the file
// pointer.
// After we've fetched the file we add the file to the zip file using
// the file pointer and then we close the curl request and the file
// pointer.
// Closing the file pointer removes the file.
$fp = tmpfile();
$ch = curl_init($signedUrl);
curl_setopt($ch, CURLOPT_TIMEOUT, 120);
curl_setopt($ch, CURLOPT_FILE, $fp);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_exec($ch);
curl_close($ch);
$zip->addFileFromStream($filename, $fp);
fclose($fp);
}
$zip->finish();
}
Note this requires curl and php-curl to be installed and functioning on your server.
I had the same issues as #cubiclewar and investigated a little bit. I found that the most up to date solution to this doesn't need curl and it visible here on the wiki for the maennchen/ZipStream-PHP/ library.
https://github.com/maennchen/ZipStream-PHP/wiki/Symfony-example
use ZipStream;
//...
/**
* #Route("/zipstream", name="zipstream")
*/
public function zipStreamAction()
{
//sample test file on s3
$s3keys = array(
"ziptestfolder/file1.txt"
);
$s3Client = $this->get('app.amazon.s3'); //s3client service
$s3Client->registerStreamWrapper(); //required
//using StreamedResponse to wrap ZipStream functionality for files on AWS s3.
$response = new StreamedResponse(function() use($s3keys, $s3Client)
{
// Define suitable options for ZipStream Archive.
$opt = array(
'comment' => 'test zip file.',
'content_type' => 'application/octet-stream'
);
//initialise zipstream with output zip filename and options.
$zip = new ZipStream\ZipStream('test.zip', $opt);
//loop keys - useful for multiple files
foreach ($s3keys as $key) {
// Get the file name in S3 key so we can save it to the zip
//file using the same name.
$fileName = basename($key);
//concatenate s3path.
$bucket = 'bucketname'; //replace with your bucket name or get from parameters file.
$s3path = "s3://" . $bucket . "/" . $key;
//addFileFromStream
if ($streamRead = fopen($s3path, 'r')) {
$zip->addFileFromStream($fileName, $streamRead);
} else {
die('Could not open stream for reading');
}
}
$zip->finish();
});
return $response;
}
In my application users creating articles and adding images to it if user wont add a image, that application must search for it in google images. I'm googling it quite long but still can't find which tools do I need to achieve this.
EDIT
I tried Mimos approach but now something going wrong and now I get:
NotReadableException in AbstractDecoder.php line 302:
Image source not readable
When i tried to save the image from url
ArticlesController store method:
public function store(ArticleRequest $request)
{
if ($request->hasFile('file')) {
$file = Input::file('file');
$imgTitle = $request->title;
$imagePath = 'uploads/' . $imgTitle . '.jpg';
$request->image_path = $imagePath;
Article::create(array('title' => $request->title,
'body' => $request->body,
'image_path' => $imagePath));
Image::make($file)->resize(300, 200)->save($imagePath);
} else {
// $file = Input::file('file');
$imgTitle = $request->title;
$query = $imgTitle;
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://ajax.googleapis.com/ajax/services/search/images?v=1.0&q=" . urlencode($query));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$output = json_decode(curl_exec($ch));
// $file = file_get_contents($output);
curl_close($ch);
$imagePath = 'uploads/' . $imgTitle . '.jpg';
$request->image_path = $imagePath;
Article::create(array('title' => $request->title,
'body' => $request->body,
'image_path' => $imagePath));
Image::make($output)->resize(300, 200)->save($imagePath);
}
}
<?php
$query = 'Foobar';
$ch = curl_init();
// set url
curl_setopt($ch, CURLOPT_URL, "https://ajax.googleapis.com/ajax/services/search/images?v=1.0&q=".urlencode($query));
//return the transfer as a string
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
// $output contains the output string as json
$output = json_deocde(curl_exec($ch));
// close curl resource to free up system resources
curl_close($ch);
But I don't recommend to let a program decide what image it should take. You will maybe get a problem with copyright. Better set a default pic.
1 - I have configure google picker and it is working fine and I select the file from picker and get the file id.
2 - After refresh token etc all process I get the file metadata and get the file export link
$downloadExpLink = $file->getExportLinks();
$downloadUrl = $downloadExpLink['application/vnd.openxmlformats-officedocument.wordprocessingml.document'];
3 - After that I use this
if ($downloadUrl) {
$request = new Google_HttpRequest($downloadUrl, 'GET', null, null);
$httpRequest = Google_Client::$io->authenticatedRequest($request);
if ($httpRequest->getResponseHttpCode() == 200)
{
$content = $httpRequest->getResponseBody();
print_r($content);
} else {
// An error occurred.
return null;
}
and get this response
[responseBody:protected] => PK��DdocProps/app.xml���
�0D���k�I[ѫ��m
��!����A={���}�
2G�Z�g�V��Bľ֧�n�Ҋ�ap!����fb�d����k}Ikc�_`t<+�(�NJ̽�����#��EU-
�0#���P����........
4 - I use some cURL functions to get file from google drive and save it to server. IN server directory a file created but cropped. I use this code
$downloadExpLink = $file->getExportLinks();
$downloadUrl = $downloadExpLink['application/vnd.openxmlformats-officedocument.wordprocessingml.document'];
//$downloadUrl value is
/*https://docs.google.com/feeds/download/documents/export/Export?id=1CEt1ya5kKLtgK************IJjDEY5BdfaGI&exportFormat=docx*/
When I put this url into browser it will download file successfully but when I use this url to fetch file with cURL or any php code and try to save it on server it saves corrupted file.
$ch = curl_init();
$source = $downloadUrl;
curl_setopt($ch, CURLOPT_URL, $source);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$data = curl_exec ($ch);
curl_close ($ch);
$destination = "test/afile5.docx";
$file = fopen($destination, "w+");
fputs($file, $data);
fclose($file);
It result a corrupted file stored on server but whe I use this code to get any file other then google drive I download it successfully on server.
Can any one please help that how to download file from $downloadUrl to my server using php ?
I have tried to use file_get_contents() and also curl to do this .. But both of these functions download the file temporarily to my pc and then upload to drive ...
Is there any way in which i can directly upload file from the url to my drive ?
Here is one code i tried :
$file = new Google_DriveFile();
$file->setTitle('My app');
$file->setDescription('Application');
$file->setMimeType('application/exe');
$url = "http://xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.exe";
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$data = curl_exec ($ch); // execute
curl_close ($ch); // close curl handle
$createdFile = $service->files->insert($file, array('data' => $data,'mimeType' => 'application/exe',));
Here is another one :
$file = new Google_DriveFile();
$file->setTitle('My app');
$file->setDescription('Application');
$file->setMimeType('application/exe');
$url = "http://xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.exe";
$data = file_get_contents($url);
$createdFile = $service->files->insert($file, array('data' => $data,'mimeType' => 'application/exe',));
Both of these codes are downloading the files first to my pc ... then they start uploading
When you pass a URL in stead of a local file path to file_get_contents() or curl_init(), PHP will automatically attempt to download the file that the URL points to.
All you need to do to prevent this is to change the value of $url to a local file path:
$url = '/tmp/somefile.txt'; // *nix
$url = 'c:/somefile.txt'; // Windows
I am using csxi to make scanning for documnets as image, but I have to upload pdf files to server. How can I convert image to PDF in php ? or is there any way to make csxi scan documents as PDF not image
If you have ImageMagick installed on your machine you could use the ImageMagick bindings for PHP to execute some simple PHP code to do this task:
$im=new Imagick('my.png');
$im->setImageFormat('pdf');
$im->writeImage('my.pdf');
Alternatively if you don't have ImageMagick available you could use a commercial API such as Zamzar which supports image to PDF conversion via PHP (more info in the docs).
Code to use this would be:
<?php
// Build request
$endpoint = "https://api.zamzar.com/v1/jobs";
$apiKey = "YOUR_API_KEY";
$sourceFilePath = "my.png";
$targetFormat = "pdf";
$sourceFile = curl_file_create($sourceFilePath);
$postData = array(
"source_file" => $sourceFile,
"target_format" => $targetFormat
);
// Send request
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $endpoint);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
curl_setopt($ch, CURLOPT_SAFE_UPLOAD, false);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_USERPWD, $apiKey . ":");
$body = curl_exec($ch);
curl_close($ch);
// Process response (with link to converted files)
$response = json_decode($body, true);
print_r($response);
?>
Wrap your image inside HTML and use some HTML to PDF converter like fpdf or mpdf
You can use convertapi service, easy to install:
composer require convertapi/convertapi-php
require_once('vendor/autoload.php');
use \ConvertApi\ConvertApi;
//get api key: https://www.convertapi.com/a/si
ConvertApi::setApiSecret('xxx');
$result = ConvertApi::convert('pdf', ['File' => '/dir/test.png']);
# save to file
$result->getFile()->save('/dir/file.pdf');
to convert multiple files and other options check https://github.com/ConvertAPI/convertapi-php
Here, Php 7.4, Laravel 7+, ImageMagick-7.1.0-Q16, and Ghostscript gs10.00.0 is used.
If any files are contained in the folder JpgToPdf then delete them. And so on.
/**
* jpg To pdf WEB
*
* #method convertJpgToPdf
*/
public function convertJpgToPdf(Request $request)
{
try {
//get list of files
$files = Storage::files('JpgToPdf');
/*get count of files and ,
* check if any files contain
* if any files contains
* then
* get the files name
* delete one by one
*/
if(count($files) >1 )
{
foreach($files as $key => $value)
{
//get the file name
$file_name = basename($value);
//delete file from the folder
File::delete(storage_path('app/JpgToPdf/'. $file_name));
}
}
if ($request->has('jpeg_file'))
{
$getPdfFile = $request->file('jpeg_file');
$originalname = $getPdfFile->getClientOriginalName();
$path = $getPdfFile->storeAs('JpgToPdf', $originalname);
}
// file name without extension
$filename_without_ext = pathinfo($originalname, PATHINFO_FILENAME);
//get the upload file
$storagePath = storage_path('app/JpgToPdf/' . $originalname);
$imagick = new Imagick();
$imagick->setResolution(300, 300);
$imagick->readImage($storagePath);
$imagick->setImageCompressionQuality( 100 );
$imagick->mergeImageLayers(Imagick::LAYERMETHOD_FLATTEN);
$imagick->setImageAlphaChannel(Imagick::ALPHACHANNEL_REMOVE);
$imagick->writeImage( storage_path('app/JpgToPdf/') . $filename_without_ext .'.pdf' );
return response()->download(storage_path('app/JpgToPdf/') . $filename_without_ext .'.pdf' );
} catch (CustomModelNotFoundException $exception) {
// Throws error exception
return $exception->render();
}
}
For just a few images, do it manually and easily with the Chrome web browser. You wont need an internet connection.
Save the following with .html extension in the same folder of your image:
<html>
<body>
<img src="image.jpg" width="100%">
</body>
</html>
Open the html file with Google Chrome,
Crtl + P, to open the print dialog
Choose Save as PDF, to save it locally
Alternatively, you could send a copy to your smatphone via Google Cloud Print