I am trying to upload a file to a Google Signed URL with cURL in PHP.
I have the file being posted from a form and can access it with the $_FILES var.
However the file that actually gets uploaded is not the right file and I think it is to do with how I am handling the tmp file.
$file_name = $_FILES['file']['name'];
$temp_name = $_FILES['file']['tmp_name'];
// $file = fopen($temp_name, 'r');
// $file = realpath($temp_name);
$request = curl_init($request_url);
curl_setopt($request, CURLOPT_RETURNTRANSFER, true);
curl_setopt($request, CURLOPT_POSTFIELDS, $file);
curl_setopt($request, CURLOPT_HTTPHEADER, array('Content-Type:text/plain','Authorization:'.$token));
curl_setopt($request, CURLOPT_CUSTOMREQUEST, 'PUT');
$response = curl_exec($request);
$errors = curl_error($request);
curl_close($request);
var_dump($response);
var_dump($errors);
The following works as expected with content-type text instead of audio even though its a wav file.
curl -v -X PUT --upload-file file.wav --header "Content-Type: text/plain" request_url
Edit
I have tried this:
$file = new CurlFile($temp_name,$mime_type,$file_name);
but this just crashes my page altogether.
I think it may be to do with how I am calling the request, so I wanted to create a cURL function that I can just pass the url and data to for all my requests like so:
$file = new CurlFile($temp_name,$mime_type,$file_name);
$result = curl_request($conn,$signed_url,$file,'text/plain','PUT');
Then my function is like this:
function curl_request($conn,$request_url,$request_data,$content_type,$request_type){
$api_username = 'API username';
$stmt = $conn->prepare("SELECT * FROM config WHERE setting=:setting");
$stmt->bindParam(':setting', $api_username);
$stmt->execute();
$result = $stmt->setFetchMode(PDO::FETCH_ASSOC);
foreach($stmt->fetchAll() as $key=>$row) {
$username = $row['value'];
}
$api_key = 'API key';
$stmt = $conn->prepare("SELECT * FROM config WHERE setting=:setting");
$stmt->bindParam(':setting', $api_key);
$stmt->execute();
$result = $stmt->setFetchMode(PDO::FETCH_ASSOC);
foreach($stmt->fetchAll() as $key=>$row) {
$key = $row['value'];
}
$data = array(
'username' => $username,
'password' => $key
);
$payload = json_encode($data);
$initial_request = curl_init('https://example.com/auth');
curl_setopt($initial_request, CURLOPT_RETURNTRANSFER, true);
curl_setopt($initial_request, CURLOPT_POSTFIELDS, $payload);
curl_setopt($initial_request, CURLOPT_HTTPHEADER, array('Content-Type:application/json'));
$initial_response = curl_exec($initial_request);
$initial_errors = curl_error($initial_request);
curl_close($initial_request);
$decoded_response = (array)json_decode($initial_response);
$token = $decoded_response['token'];
$request = curl_init($request_url);
curl_setopt($request, CURLOPT_RETURNTRANSFER, true);
curl_setopt($request, CURLOPT_POSTFIELDS, $request_data);
curl_setopt($request, CURLOPT_HTTPHEADER, array('Content-Type:'.$content_type,'Authorization:'.$token));
curl_setopt($request, CURLOPT_CUSTOMREQUEST, $request_type);
$response = curl_exec($request);
$errors = curl_error($request);
curl_close($request);
if(empty($errors)){
return $response;
}else{
return $errors;
}
}
This works when I had $file = fopen($temp_name, 'r'); but the file uploaded was a weird file.
Edit 2
This is what the file looks like when the person at the other end of this API tries to open it.
this is what you want: remove CURLOPT_POSTFIELDS altogether, and replace CURLOPT_CUSTOMREQUEST=>'PUT' with CURLOPT_UPLOAD=>1 and replace 'r' with 'rb', and use CURLOPT_INFILE (you're supposed to use INFILE instead of POSTFIELDS),
$fp = fopen($_FILES['file']['tmp_name'], "rb");
curl_setopt_array($ch,array(
CURLOPT_UPLOAD=>1,
CURLOPT_INFILE=>$fp,
CURLOPT_INFILESIZE=>$_FILES['file']['size']
));
This works when I had $file = fopen($temp_name, 'r');
never use the r mode, always use the rb mode (short for "binary mode"), weird things happen if you ever use the r mode on Windows, r is short for "text mode" - if you actually want text mode, use rt (and unless you really know what you're doing, you don't want the text mode, ever, unfortunate that it's the default mode),
but the file uploaded was a weird file. (...) This is what the file looks like when the person at the other end of this API tries to open it.
well you gave CURLOPT_POSTFIELDS a resource. CURLOPT_POSTFIELDS accepts 2 kinds of arguments, #1: an array (for multipart/form-data requests), #2: a string (for when you want to specify the raw post body data), it does not accept resources.
if the php curl api was well designed, you would get an InvalidArgumentException, or a TypeError, when giving CURLOPT_POSTFIELDS a resource. but it's not well designed. instead, what happened is that curl_setopt implicitly casted your resource to a string, hence resource id #X , it's the same as doing
curl_setopt($request, CURLOPT_POSTFIELDS, (string) fopen(...));
Related
I have two parts of my project. The first one is PHP part, that takes file and send it to the second part. Here is my code:
$file = fopen('file.wav', 'r');
$size = filesize('file.wav');
$ex_id = getExternalId();
$apiurl = 'http://url.to.the.second.part/?id='.$ex_id.'&oneway=true';
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $apiurl);
curl_setopt($ch, CURLOPT_PUT, 1);
curl_setopt($ch, CURLOPT_INFILE, $file);
curl_setopt($ch, CURLOPT_INFILESIZE, $size);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$result = curl_exec($ch);
curl_close($ch);
fclose($file);
//apply logic to $result
exit(0);
The second part is API part on nodejs. I can handle requests, and get id parameter from query, but req.body is undefined:
const express = require('express');
const app = express();
app.put('api.url', (req, res) => {
const externalId = req.query.id;
//do some stuff here...
//and here i'm expecting uploaded file
//but i got nothing here
const file = req.body;
//other logic here...
res.status(200).send(result);
}
What am i doing wrong?
You are opening the file using fopen fun which returns file pointer resource. You need to get the contents of the file and for that you can use file_get_contents function and then pass the file contents in the request parameter.
$file = file_get_contents('file.wav');
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 want to send data from server 1 to server 2, first I select necessary data from the database, but how to send data with curl? I understand that I cannot send $result parameter just like in my code, but how should I do this?
My Code server 1:
public function setDivisions(){
$result = $this->conn->query("SELECT *FROM data_divisions");
$ch = curl_init('https://example.com/api.php?siteid='.$this->site_key.'');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $result);
curl_setopt($ch, CURLOPT_POST, 1);
$response = curl_exec($ch);
print_r($response);
}
Code on server 2:
$array = $_POST['result'];
//loop throw array and insert data into database
you can use it that way.
$ch = curl_init('https://upxxx.cod3fus1ontm.com/curl/json');
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode((object)["records" => json_encode($result)]));
$response = curl_exec($ch);
var_dump($response);
on receipt, like this!
$json = file_get_contents("php://input");
$content = json_decode($json, true);
$records = json_decode($content['records'], true);
foreach($records as $record) {
echo $record['id'] . " - " . $record['text'] . "<br/>";
}
remember, that as you did to encode, you will have to do to decode
Come on, php://input returns all raw data after the request's HTTP headers, regardless of content type.
When we do this with file_get_contents (which reads the contents of a file and puts it into a variable of type string), we can read the content that was sent by curl.
Reviewing the code, you can optimize as follows, when sending to the server you placed, I suggested:
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode((object)["records" => json_encode($result)]));
you can replace it with:
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($result));
it's much simpler, but it's very important to put the query result inside a json_encode
finally, you can access the entire body of the message, through file_get_contents ("php://input") and put it inside a variable, which is nothing more than a JSON of the your query.
for you to understand how the parameters were passed, it is interesting to do the following:
$json = file_get_contents("php: // input");
var_dump($json); <- Here you see the thing as it is.
$records = json_decode($json, true); <- Here you generate an object with the content of json
var_dump($records);
With that, I think that solves the situation.
on server 1
$result = "result=".base64_encode($result)
curl_setopt($ch, CURLOPT_POSTFIELDS, $result);
...
on server 2
$array = base64_decode($_POST['result']);
I am having this problem from last few days to develop a CURL request in php to post file data to an API.
Here is the CURL request
$ curl --request POST \
--url 'UPLOAD_URL' \
--upload-file 'PATH_TO_FILE' \
--header 'Authorization: Bearer YOUR_ACCESS_TOKEN'
for PATH_TO_FILE I have tried each and every method published over the web and stackoverflow too.
here is my PHP code
<?php
header('Content-Type: application/json');
$sheader = array('Authorization: Bearer '.$_SESSION['access_token']);
$filename = $_FILES['upload-file']['name'];
$filedata = $_FILES['upload-file']['tmp_name'];
$filesize = $_FILES['upload-file']['size'];
$filetype = $_FILES['upload-file']['type'];
if($filename != '')
{
$ch = curl_init();
$cFile = new CURLFile(realpath($filename), $filetype, 'harish.jpg');
//$cFile = '#'. realpath($filename);
$data = array('upload-file' => $cFile);
curl_setopt($ch, CURLOPT_POSTFIELDS, $cFile);
//curl_setopt($ch, CURLOPT_POSTFIELDS, realpath($filename));
curl_setopt($ch, CURLOPT_URL, $_POST['upload_url']);
curl_setopt($ch, CURLOPT_HTTPHEADER, $sheader);
curl_exec($ch);
echo json_encode($ch);
//echo json_encode(array('filename'=>$filename, 'temp_path'=>$filedata, 'basepath'=>realpath($filename)));
} else {
echo 'There is no file selected';
}
Mostly the solution i have found on the web are these two mentioned below
Method 1 (for php < 5.5)
'#'.$filepath
Method 2 (for php > 5.5)
CURLFile($filename, $filetype, 'somefile.jpg');
or
curl_file_create($filedata, 'image/jpeg', $filename);
None of the above worked for me. I have use realpath($filename) too inside CURLFile to fetch absolute path of the file, but sadly that also not worked.
I admit that CURLFile documentation is slightly ambiguous but the first constructor argument, $name, is in fact the physical path to the actual file you want to send, not the "friendly" name (which goes in the optional third argument, $postname). You should note there's something wrong since you never tell Curl about $_FILES['upload-file']['tmp_name']—it has no way to know what file you want to send.
So:
$filename = $_FILES['upload-file']['name'];
$filedata = $_FILES['upload-file']['tmp_name'];
$filesize = $_FILES['upload-file']['size'];
$filetype = $_FILES['upload-file']['type'];
$cFile = new CURLFile($filedata, $filetype, 'harish.jpg')
You aren't being notified about this because you're skipping error checking. You don't check the return value of any function, neither call curl_error() anywhere in your code.
One more error you have is that you pass the CURLFile instance this way:
curl_setopt($ch, CURLOPT_POSTFIELDS, $cFile);
Correct syntax should be like:
curl_setopt($ch, CURLOPT_POSTFIELDS, ['give_a_nice_post_name_here' => $cFile]);
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');