I want to upload a file from an external URL directly to an Amazon S3 bucket using the PHP SDK. I managed to do this with the following code:
$s3 = new AmazonS3();
$response = $s3->create_object($bucket, $destination, array(
'fileUpload' => $source,
'length' => remote_filesize($source),
'contentType' => 'image/jpeg'
));
Where the function remote_filesize is the following:
function remote_filesize($url) {
ob_start();
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, 1);
curl_setopt($ch, CURLOPT_NOBODY, 1);
$ok = curl_exec($ch);
curl_close($ch);
$head = ob_get_contents();
ob_end_clean();
$regex = '/Content-Length:\s([0-9].+?)\s/';
$count = preg_match($regex, $head, $matches);
return isset($matches[1]) ? $matches[1] : "unknown";
}
However, it would be nice if I could skip setting the filesize when uploading to Amazon since this would save me a trip to my own server. But if I remove setting the 'length' property in the $s3->create_object function, I get an error saying that the 'The stream size for the streaming upload cannot be determined.' Any ideas how to solve this problem?
You can upload file from url directly to Amazon S3 like this (my example is about a jpg picture):
1. Convert the content from url in binary
$binary = file_get_contents('http://the_url_of_my_image.....');
2. Create an S3 object with a body to pass the binary into
$s3 = new AmazonS3();
$response = $s3->create_object($bucket, $filename, array(
'body' => $binary, // put the binary in the body
'contentType' => 'image/jpeg'
));
That's all and it's very fast. Enjoy!
Do you have any control over remote server/host?. If so you could set up a php server to query the file locally and pass the data to you.
If not, you could use something like curl to inspect the header like so;
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'http://sstatic.net/so/img/logo.png');
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_NOBODY, true);
curl_exec($ch);
$size = curl_getinfo($ch, CURLINFO_CONTENT_LENGTH_DOWNLOAD);
var_dump($size);
This way, you are using a HEAD request, and not downloading the whole file -- still, you depend on the remote server send a correct Content-length header.
Related
I know there are similar cases to my problem but my case is about uploading a file with the WorkDocs API in php.
I saw that this problem could be related to the file size but mine is only 1KB.
I also saw that it could be a cURL bug (source: https://github.com/aws/aws-sdk-php/issues/29).
I call the initiateDocumentVersionUpload service and get the upload url:
$result = $client->initiateDocumentVersionUpload([
"Name" => "test-file.txt",
"ParentFolderId" => "***"
]);
$content = $result->get('UploadMetadata');
$url = $content['UploadUrl'];
And my curl request:
// The full path to the file that you want to upload
$filePath = 'C:/wamp64/www/test_aws/test-file.txt';
// Initiate cURL
$curl = curl_init($url);
// Set the URL
curl_setopt($curl, CURLOPT_URL, $url);
// Set the HTTP request to POST
curl_setopt($curl, CURLOPT_PUT, true);
//Tell cURL to return the output as a string.
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
//Open the file using fopen.
$fileHandle = fopen($filePath, 'r');
//Pass the file handle resorce to CURLOPT_INFILE
curl_setopt($curl, CURLOPT_INFILE, $fileHandle);
//Set the CURLOPT_INFILESIZE option.
curl_setopt($curl, CURLOPT_INFILESIZE, filesize($filePath));
$headers = array(
"Content-Type: application/octet-stream",
"x-amz-server-side-encryption: AES256"
);
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
$data = "PUT request data";
curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
// for debug only!
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
$resp = curl_exec($curl);
curl_close($curl);
var_dump($resp);
And so I get the error: Your socket connection to the server was not read from or written to within the timeout period. Idle connections will be closed.
Do you know how I can fix this problem and upload my file ? Thanks in advance !
I've found another solution that works perfectly.
I've add the attribut 'verify' => false to the httpClient instance to avoid SSL issue.
$filePath = 'C:/wamp64/www/test_aws/test-file.txt';
$body = fopen($filePath, 'r');
$guzzle = new httpClient(['verify' => false]);
$upload = $guzzle->put($uploadUrl, [
'headers' => [
"Content-Type" => "application/octet-stream",
"x-amz-server-side-encryption" => "AES256"
],
'body' => $body
]);
Found the solution here: https://docs.aws.amazon.com/code-samples/latest/catalog/php-workdocs-UploadDocument.php.html
I am using PHP to get my object from AWS S3 and then turn around and upload it into Dropbox. I can successfully get the object from AWS, but when I try and upload the file into Dropbox I just an empty Json reponse with no error or anything. I know that my $dbToken (Dropbox token) is working right because I can use the same key to get my folder structure and list files.
When I try and upload a file it just goes blank. I think it has to do with the AWS presigned URL. Do I need to make a Curl request to get the contents of that url ($secureUrl in my case). Not sure how to write that and how to get this file into Dropbox. Thanks for your help.
# ============================================
# Connect to AWS and get the Object
# This works fine...
# ============================================
$s3 = new S3Client([
'credentials'=>[
'key'=>xxxxxxx,
'secret'=>xxxxxxx,
],
'region'=>'us-west-2',
'version'=>'2006-03-01',
]);
$cmd = $s3->getCommand('GetObject', [
'Bucket' => xxxxxxx,
'Key' => xxxxxxx,
]);
$request = $s3->createPresignedRequest($cmd, '+60 seconds');
$secureUrl = (string) $request->getUri(); // get signed uri...
# echo $secureUrl; // if I echo this on the page, I can see the url, copy it and paste it into the browser and all works well...
# ============================================
# Connect To DropBox and Upload File...
# This is returning nothing in my output...
# ============================================
$dbToken = 'xxxxxxx';
$headers = [
"Authorization: Bearer ". $dbToken,
'Dropbox-API-Arg: {"path": "'.$secureUrl.'", "mode": "add", "autorename": true, "mute":false }',
"Content-Type: application/octet-stream"
];
$url = 'https://content.dropboxapi.com/2/files/upload';
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_POST, true);
$path = $secureUrl;
$fp = fopen($path, 'rb');
curl_setopt($ch, CURLOPT_INFILE, $fp);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_VERBOSE, 1);
$result = curl_exec($ch);
$data = json_decode(trim($result), TRUE);
echo '<pre>';
print_r($data); // the json response is empty...
echo '</pre>';
I'm using the Google App Engine (GAE) for PHP, and I'm trying to use the Mailgun API to send a message with an attachment using CURL.
The attachment is on Google Cloud Storage (because there are limitations on GAE for writing and reading files on a local filesystem). So what I'm doing is using a temporary file instead.
Here's my code so far:
$url_str = 'https://api.mailgun.net/v3/es.restive.io/messages';
$auth_user_str = 'api';
$auth_pass_str = 'key-my-unique-key';
$post_data_arr = array(
'from' => 'Sender <info#email.com>',
'to' => 'recipient#email.com',
'subject' => 'Test Mail from GAE',
'html' => '<html><body><strong><p>a simple HTML message from GAE</p></strong></body></html>',
'o:tracking' => 'yes'
);
$headers_arr = array("Content-Type:multipart/form-data");
$file_gs_str = 'gs://my_bucket/attachment.pdf';
$tmp_path = tempnam(sys_get_temp_dir(), '');
$handle = fopen($tmp_path, "w");
fwrite($handle, file_get_contents($file_gs_str));
fseek($handle, 0);
$post_data_arr['attachment'] = curl_file_create($tmp_path, 'application/pdf', 'proposal.pdf');
$cl = curl_init();
curl_setopt($cl, CURLOPT_URL, $url_str);
curl_setopt($cl, CURLOPT_TIMEOUT, 30);
curl_setopt($cl, CURLOPT_HTTPHEADER, $headers_arr);
curl_setopt($cl, CURLOPT_POST, true);
curl_setopt($cl, CURLOPT_POSTFIELDS, $post_data_arr);
curl_setopt($cl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($cl, CURLOPT_USERPWD, "$auth_user_str:$auth_pass_str");
$status_code = curl_getinfo($cl);
$response = curl_exec($cl);
fclose($handle);
curl_close($cl);
For some reason, it doesn't work.
I've made sure the temp file is actually generated by putting it back into google cloud storage using this code:
$options = ['gs' => ['Content-Type' => 'application/pdf']];
$ctx = stream_context_create($options);
file_put_contents('gs://my_bucket/re_attachment.pdf', file_get_contents($tmp_path), 0, $ctx);
When I run the above I'm simply taking the temporary file and uploading it back to Google Cloud Storage using a different name, and then I download it and open it to make sure it's the same as the original. No issues here.
Unfortunately, I can't seem to get CURL to work with it. When I comment out $post_data_arr['attachment'] = curl_file_create($tmp_path, 'application/pdf', 'proposal.pdf');, the message is sent, albeit with no attachment.
How can I get this to work?
First, make sure you run PHP 5.5, because curl_file_create() is only supported from PHP 5.5.
Then, try to get rid of setting headers explicitly. When the value of CURLOPT_POSTFIELDS is an array, then Curl automatically sets a header for multipart/form-data. So, get rid of this:
curl_setopt($cl, CURLOPT_HTTPHEADER, $headers_arr);
Does any one know how to check media types of audio and video from external urls.
function get_url_mime_type($url) {
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_HEADER, 1);
curl_setopt($ch, CURLOPT_NOBODY, 1);
curl_exec($ch);
return curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
}
To get file's MIME, use the following codes:
<?php
$finfo = new finfo(FILEINFO_MIME, "/usr/share/misc/magic"); // return mime type a.k.a. mimetype extension
/* get mime-type for a specific file */
$filename = "/usr/local/something.txt";
echo $finfo->file($filename);
?>
To get the extension of the file:
$tokens = explode('.', $filename);
$extension = $tokens[count($tokens)-1];
To get the MIME type of external file, either you can use cURL HEAD request to get the header ( may not be accurate , as web server may not response with correct answer ), or use getID3.
This question also asks the similar thing. Take a look of it.
Reference: finfo_open()
I am currently adding the ability to a php back-end system to allow it to print directly and I am trying to get things working with Google's Cloud Print. Imagine the app as an online shopping cart and I want it to print picking notes (completed orders) without the need for someone to login. The server is remote and the destination has Cloud Ready Printers.
So far I have been successful in getting it to print using the interfaces, as long as I am simply passing HTML, plain text or a URL to a PDF. I am able to set the print to color, marginless and the print quality.
However where I have hit a problem is, the PDF which the system creates are not publicly accessible, hence I can't pass a URL to the file, I need to pass the contents of the file.
I have been trying with no success to modify one of the examples I have found on the web HERE. However I don't know the language so am struggling with it.
Another example in python HERE again I have been trying without success!
I'm using PHP and the Zend framework to work with the interface. Here is one sample I have tried, cut down to where I am trying to prepare the file to send, like I say I'm not really sure on translating from python to php, or if the python script even works, but this is what I came up with:
<?php
// Test print a job:
$b64_pathname = PDF_PATH.'ec22c3.pdf'.'.b64';
$fileType = "application/pdf";
// Open the original file and base64 encode it:
$dataHandle = fopen(PDF_PATH.'ec22c3.pdf', "rb");
$dataContent = fread($dataHandle, filesize(PDF_PATH.'ec22ed167763a15e8591a3776f3c65c3.pdf'));
fclose($dataHandle);
$b64data = $fileType.base64_encode($dataContent);
// Store the base64 encoded file:
$ourFileHandle = fopen($b64_pathname, 'w');
fwrite($ourFileHandle, $b64data);
fclose($ourFileHandle);
// Read the contents of the base64 encoded file and delete it:
$fileHandle = fopen($b64_pathname, "rb");
$fileContent = fread($fileHandle, filesize($b64_pathname));
fclose($fileHandle);
unlink($b64_pathname);
// URL encode the file contents:
$file = urlencode($fileContent);
// Add the file and send to the printer:
$client->setParameterPost('content', $file);
$client->setParameterPost('contentType', $fileType);
$client->request(Zend_Http_Client::POST);
?>
Here's a method in php using cUrl (note, I have object level variables called _auth, _username, _password & _printerId).
First, build a function to post with cUrl:
function processRequest($url, $postFields, $referer) {
$ret = "";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL,$url);
curl_setopt($ch, CURLOPT_USERAGENT, "");
if(!is_null($postFields)) {
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS,
$postFields);
// http_build_query() will properly escape the fields and
// build a query string.
}
if(strlen($this->_auth) > 0) {
$headers = array(
"Authorization: GoogleLogin auth=". $this->_auth,
//"GData-Version: 3.0",
"X-CloudPrint-Proxy", "yourappname"
);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
}
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($ch, CURLOPT_REFERER, $referer);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
$ret = curl_exec ($ch);
curl_close ($ch);
return $ret;
}
Then, a function to authorize against Google:
public function authorize() {
$url = "https://www.google.com/accounts/ClientLogin";
$post = array("accountType" => "HOSTED_OR_GOOGLE",
"Email" => $this->_username,
"Passwd" => $this->_password,
"service" => "cloudprint",
"source" => "yourappname");
$resp = $this->processRequest($url, $post, "");
preg_match("/Auth=([a-z0-9_\-]+)/i", $resp, $matches);
$this->_auth = $matches[1];
}
Finally, build a function to submit to the cloud printer:
function printDocument($title, $docBytes)
{
$url = "http://www.google.com/cloudprint/submit?printerid=". $this->_printerId."&output=json";
$post = array(
"printerid" => $this->_printerId,
"capabilities" => "",
"contentType" => "dataUrl",
"title" => $title,
"content" => 'data:application/pdf;base64,'. base64_encode($docBytes)
);
$ret = $this->processRequest($url, $post, "");
echo $ret;
}
In use, call authorize() to get the authentication token. Then just read your file (from wherever) into a variable and pass it to printDocument with the title.
In order to send base64 encoded content you need to send another parameter in submit request:
$client->setParameterPost('contentTransferEncoding', 'base64');