How to stream binary data from file to POST body in PHP - php

I'm not a PHP expert so please excuse the lack of correct terminology. Here is some background: There is a service running somewhere on the web and I have to communicate with it via REST API. The API requires me to send binary data (more specifically a ZIP) via POST request. The response will return other binary data that is to be saved in a file.
Since the request data and the response data may be arbitrarily large, I want to stream or buffer the data while reading it from and saving it in a file. The request has to be done in PHP.
Below is a code snippet I use to perform the POST request and handle the response (nevermind missing error handling). As you can see I have two files: "request-data" is a file containing the data I like to POST, "response-data" is the file where I write the response.
$binaryRequestFile = fopen('request-data', 'rb');
$binaryResponseFile = fopen('response-data', 'wb');
$url = "http://some/url";
$headerStr .= "Content-Type: application/zip\r\n";
$options = array(
'http' => array(
'header' => $headerStr,
'follow_location' => false,
'max_redirects' => 0,
'method' => 'POST',
'content' => stream_get_contents($binaryRequestFile),
'ignore_errors' => true
),
);
$context = stream_context_create($options);
$responseData = fopen($url, false, false, $context);
while (!feof($responseData)) {
if (fwrite($binaryResponseFile, fread($responseData, 4 * 1024)) === FALSE) {
return null;
}
}
fclose($responseData);
The code above works, but I'd rather not use stream_get_contents to read the whole request data into memory. Isn't there a way to stream the data directly into the request similar to what is done with the response? I prefer using built-in PHP functionality, no external modules or CURL.

Related

How do I know if I have received a POST request from a webhook to my PHP file?

My file, request.php, makes a POST request to an API and immediately gets a response saying that the request is approved.
The POST request sends a callback url to which the API will send that JSON response. I am using receive.php as the callback url.
When I use a unique webhook.site/id as callback url I can see the JSON response. So I know that the API request works.
My problem is that receive.php is empty after every POST request. Why is there no JSON response to receive.php?
On my file receive.php I have the following code:
$body = file_get_contents('php://input');
$webhook = json_decode($body);
I have tried displaying the response with print_r, var_dump, echo etc.
The code on request.php:
<?php
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => 'https://api-url-with-my-key',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => '',
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 0,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_POSTFIELDS =>'{
"pno": "number",
"callbackUrl": "https://example.com/receive.php",
"ipAddress": "8.8.8.8",
"refID": "text123456"
}',
CURLOPT_HTTPHEADER => array(
'Content-Type: application/json',
),
));
$response = curl_exec($curl);
curl_close($curl);
echo $response;
?>
In chrome I can see the following warning "Resource interpreted as Document but transferred with MIME type application/json:"
EDIT:
The response from the API:
{
"result": "completed",
"orderRef": "98308bf7-c8be-4da6-8491-9f599d437de9",
"pno": "number",
"name": "a name",
"checksum": "number",
"certStartDate": "2021-01-28",
"refID": ”text123456"
}
If you're saying there's a problem with the webhook, then what needs to be investigated is what happens when the other API calls https://example.com/receive.php. That's a separate request, with a separate payload. Such a request will happen at some later time, and the PHP code in receive.php will process it.
Since that will be unattended, you probably need to make receive.php do something with the received data so you can see it afterwards, such as store it in a file or a database, or email it to yourself. echoing it won't be much use because it's only the remote API which would see that response.
I have now solved the problem. The web host have a firewall that I cannot manage or see, after adding the API IP-address everything works.
I did call them some week ago asking for this but they could help me then.
This code collects the response.
$body = file_get_contents('php://input');
$webhook = json_decode($body);

Stream response to download CSV file

I am trying to stream a large CSV from the server. I load a JSON file, convert it to an array, and then process it as a CSV. From the frontend, I am calling the following on a button click
downloadCSVData() {
axios({
url: '/downloadCSVData',
method: 'GET'
}).then((response) => {
console.log(response)
});
}
And then this function does the following
public function downloadCSVData()
{
//load JSON file
$data = file_get_contents($file_path, true);
//Convert file to array
$array = json_decode($data, true);
$headers = [
'Cache-Control' => 'must-revalidate, post-check=0, pre-check=0'
, 'Content-type' => 'text/csv'
, 'Content-Disposition' => 'attachment; filename=galleries.csv'
, 'Expires' => '0'
, 'Pragma' => 'public'
];
$response = new StreamedResponse(function() use($array){
// Open output stream
$handle = fopen('php://output', 'w');
// Add CSV headers
fputcsv($handle, array_keys($array['element'][0]), ',');
foreach ($array['element'] as $key => $row) {
fputcsv($handle, $row);
}
// Close the output stream
fclose($handle);
}, 200, $headers);
return $response->send();
}
Now on the frontend, within the console, I can see the reponse being printed the way it needs to be. From my understanding however, the backend should trigger the file to be downloaded which is not happening.
Am I missing something here, how can I get this downloaded on the frontend as a physical file?
Thanks
If you visited the URL directly, your PHP settings should work to force a download. But you are already in a fully loaded HTML page, so you need to use the <iframe> trick to get the file to download. This will allow the browser to receive the incoming data as if you've opened a new browser window.
Try changing your download function to this:
downloadCSVData() {
$('<iframe />')
.attr('src', '/downloadCSVData')
.hide()
.appendTo('body');
}

Having some issue with ajax and PHP

I am trying to get the ajax data in PHP variable. The code is working fine and I am able to get the ajax data in PHP variable.
My question is : Can I use the PHP variable for file_get_contents function again ?
My PHP code is like this
<?php
$html = file_get_contents('https://someurl.com');
if (isset($_POST['job']))
{
$job = $_POST['job'];
$attrb = $_POST['attrb'];
echo $job;
echo $attrb;
$htmlcontents = file_get_contents($attrb);
}
?>
And the Ajax code is as below
$(document).ready(function(){
$.post("test.php",
{
job: exactdatainner,
attrb: getattr
},
function(data,status){
var obj = data.split('http');
var title = obj[0];
var link = 'http' + obj[1];
$(".job").html(title);
$(".attribute").html(link);
});
});
This code works fine for the first step, sending data from ajax and receiving response and print the result in a Div.
Now I am trying to fetch the URL (this URL was created in first step and stored in a PHP variable. Code is : $attrb = $_POST['attrb'];)
As you can see that I am able to print the value in first step, but when I am trying to get the contents of URL again, it gives me the error.
Try below code, I tested and it's working. Returning a 404 response <h1>Sorry This Page is Not Available Now</h1> and I also verified the response by hitting the same url in the browser, so it's working.
file_get_contents() stops with a warning errors for non-2xx status codes.
You just need to fetch the contents even on failure status code maybe 404 or 500, for that you need to set ignore_errors => true,default is false.
echo file_get_contents(
'https://www.sarkariresult.com/force/navy-sst',
false,
stream_context_create([
'http' => [
'ignore_errors' => true
],
]) );
More information have a look on this question, second answer. here
Update
$url = 'https://html5andcss3.org/';
$opts = array(
'http'=>array(
'method'=>"GET",
'header'=>"Accept-language: en\r\n",
'ignore_errors' => true, //set to true for non 2XXX reponse codes
)
);
$context = stream_context_create($opts);
// Open the file using the HTTP headers set above
$file = file_get_contents($a, false, $context);//pass the variable $url
echo $file;

How can I store a returned google app script "doPost(e)" function in a variable in cURL?

I have written a doPost(e) function on google app script that will return a number depending on the data that is sent through. The script returns a number, somewhat like this:
Google App Script Code:
doPost(e)
{
/* Code that does stuff with the parameters */
var results = 3;
var result_output = JSON.stringify(results);
var output = ContentService.createTextOutput(result_output).setMimeType(ContentService.MimeType.JSON);
return output;
}
This is my cURL code which sends appropriate values and aims to store the returned output from the Google App Script code in a variable to be used later in the program:
function connect_to_GAS()
{
// Get cURL resource
$curl = curl_init();
// Set some options
curl_setopt_array($curl, array(
CURLOPT_URL => 'puturlhere',
CURLOPT_POST => 1,
CURLOPT_POSTFIELDS => array(
// add params here
)
));
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); // there is a redirect
// Send the request, save response to $resp
$resp = curl_exec($curl);
echo $resp;
}
When I return the $resp value it only ever returns "1" because the function was executed succesfully, but I can't get it to store the retuned value for some reason!!!
I have deployed it as a web app, and the Post function of the script does work, but my cURL side is not receiving the value as i'd like it to. Frustratingly, when I run my cURL script the value I need is displayed in the resultant web page but I have no way to access it.
Does anyone know how I can store the response????
Any help is MUCH appreciated!!
First thing is that when ever you make a changes in your script, then it need to be saved as a new version and published again to get
it effective.
You are returning a number, but not a json. Try returning a proper json.
For such cases I use guzzle.
The 1 that you are getting as result has nothing to do with variable being returned from gs file. That 1 indicates curl has gone success.
Sample Code
In gs file
function doPost(e){
//WHILE PRODUCTION
//var postData = JSON.parse(e.postData.contents);
//WHILE TESTING
//var postData = e.postData.contents;
return ContentService.createTextOutput(JSON.stringify({'status': 'success'})).
setMimeType(ContentService.MimeType.JSON);
}
function test(){
var payload = {
"key1": "value1",
"key2": "value2",
}
var e = {
postData: {
contents: payload
}
};
var returned = doPost(e);
}
In php file
<?php
session_start();
require_once '../vendor/autoload.php';
$client = new GuzzleHttp\Client(['verify'=>false]);
// Webapp link
$url = 'https://script.google.com/macros/s/yourwebapplink/exec';
$payload = json_encode(array("key1" => "value1", "key2" => "value2"))
$response = $client->request('POST', $url, ['json' => $payload]);
$body = json_decode($response->getBody());
print_r($body);
return;
?>
It is easy to install guzzle if you are using composer

PHP POST request via AJAX returns source code

I am trying to send POST and retrieve data from that POST. Instead I get the source code of my AJAX script (ajax.php).
<?php
$params = array('action' => 'LOL');
$query = http_build_query($params);
$contextData = array(
'method' => 'POST',
'content' => $query
);
$context = stream_context_create(array('http' => $contextData));
$result = file_get_contents(
'ajax.php',
false,
$context
);
var_dump($result);
?>
What gives?
The typical use of ajax is to have a call from javascript (in the client browser), submit a request to the server. The server would then run some code (e.g. written in php) and return a result that the client code then uses to do something useful (or not do anything - that's possible just as well).
You seem to only have php code that's trying to call "ajax.php" ...
as to why you get the code, instead of it executing: file_get_contents read the content of a file ...
More info: http://php.net/manual/en/function.file-get-contents.php

Categories