I am trying to learn how to transfer files (.zip files) between a client and server using PHP and SOAP. Currently I have a set up that looks something like this:
require('libraries/nusoap/nusoap.php');
$server = new nusoap_server;
$server->configureWSDL('server', 'urn:server');
$server->wsdl->schemaTargetNamespace = 'urn:server';
$server->register('sendFile',
array('value' => 'xsd:string'),
array('return' => 'xsd:string'),
'urn:server',
'urn:server#sendFile');
But I am unsure on what the return type should be if not a string? I am thinking of using a base64_encode.
To be more clear I have posted both the server.php code and client.php code. Please see below:
## server.php ##
require_once('lib/nusoap.php'); //include required class for build nnusoap web service server
// Create server object
$server = new soap_server();
// configure WSDL
$server->configureWSDL('Upload File', 'urn:uploadwsdl');
// Register the method to expose
$server->register('upload_file', // method
array('file' => 'xsd:string','location' => 'xsd:string'), // input parameters
array('return' => 'xsd:string'), // output parameters
'urn:uploadwsdl', // namespace
'urn:uploadwsdl#upload_file', // soapaction
'rpc', // style
'encoded', // use
'Uploads files to the server' // documentation
);
// Define the method as a PHP function
function upload_file($encoded,$name) {
$location = "uploads\\".$name; // Mention where to upload the file
$current = file_get_contents($location); // Get the file content. This will create an empty file if the file does not exist
$current = base64_decode($encoded); // Now decode the content which was sent by the client
file_put_contents($location, $current); // Write the decoded content in the file mentioned at particular location
if($name!="")
{
return "File Uploaded successfully..."; // Output success message
}
else
{
return "Please upload a file...";
}
}
// Use the request to (try to) invoke the service
$HTTP_RAW_POST_DATA = isset($HTTP_RAW_POST_DATA) ? $HTTP_RAW_POST_DATA : '';
$server->service($HTTP_RAW_POST_DATA);
=====================================================================
## client.php ##
require_once('lib/nusoap.php'); //include required class for build nnusoap web service server
$wsdl="http://localhost:81/subhan/webservice3/server.php?wsdl"; // SOAP Server
if($_POST['submit'])
{
$tmpfile = $_FILES["uploadfiles"]["tmp_name"]; // temp filename
$filename = $_FILES["uploadfiles"]["name"]; // Original filename
$handle = fopen($tmpfile, "r"); // Open the temp file
$contents = fread($handle, filesize($tmpfile)); // Read the temp file
fclose($handle); // Close the temp file
$decodeContent = base64_encode($contents); // Decode the file content, so that we code send a binary string to SOAP
}
$client=new soapclient($wsdl) or die("Error"); // Connect the SOAP server
$response = $client->__call('upload_file',array($decodeContent,$filename)) or die("Error"); //Send two inputs strings. {1} DECODED CONTENT {2} FILENAME
// Check if there is anny fault with Client connecting to Server
if($client->fault){
echo "Fault {$client->faultcode} <br/>";
echo "String {$client->faultstring} <br/>";
}
else{
print_r($response); // If success then print response coming from SOAP Server
}
<form name="name1" method="post" action="" enctype="multipart/form-data">
<input type="file" name="uploadfiles"><br />
<input type="submit" name="submit" value="uploadSubmit"><br />
</form>
=================================================
All you need to do is download the nusoap.php which will be seen in soap library http://sourceforge.net/projects/nusoap/
This is fully tested and it will 100% work without fail.
In client.php, change this:
if($_POST['submit'])
{
...
}
$client=new soapclient($wsdl) or die("Error"); // Connect the SOAP server
$response = $client->__call('upload_file',array($decodeContent,$filename)) or die("Error"); //Send two inputs strings. {1} DECODED CONTENT {2} FILENAME
to this:
if($_POST['submit'])
{
...
$client=new soapclient($wsdl) or die("Error"); // Connect the SOAP server
$response = $client->__call('upload_file',array($decodeContent,$filename)) or die("Error"); //Send two inputs strings. {1} DECODED CONTENT {2} FILENAME
}
Transferring files over SOAP is something that gets everybody the first time (myself included). You need to open and read the document and then transfer it as a string. Here's how I would do it.
$handle = fopen("mypackage.zip", "r");
$contents = fread($handle, filesize("mypackage.zip"));
fclose($handle);
//$contents now holds the byte-array of our selected file
Then send $contents as your string through SOAP and reassemble it on the other side.
Related
I have a JSON file badly formatted (doc1.json):
{"text":"xxx","user":{"id":96525997,"name":"ss"},"id":29005752194568192}
{"text":"yyy","user":{"id":32544632,"name":"cc"},"id":29005753951977472}
{...}{...}
And I have to change it in this:
{"u":[
{"text":"xxx","user":{"id":96525997,"name":"ss"},"id":29005752194568192},
{"text":"yyy","user":{"id":32544632,"name":"cc"},"id":29005753951977472},
{...},{...}
]}
Can I do this in a PHP file?
//Get the contents of file
$fileStr = file_get_contents(filelocation);
//Make proper json
$fileStr = str_replace('}{', '},{', $fileStr);
//Create new json
$fileStr = '{"u":[' . $fileStr . ']}';
//Insert the new string into the file
file_put_contents(filelocation, $fileStr);
I would build the data structure you want from the file:
$file_path = '/path/to/file';
$array_from_file = file($file_path);
// set up object container
$obj = new StdClass;
$obj->u = array();
// iterate through lines from file
// load data into object container
foreach($array_from_file as $json) {
$line_obj = json_decode($json);
if(is_null($line_obj)) {
throw new Exception('We have some bad JSON here.');
} else {
$obj->u[] = $line_obj;
}
}
// encode to JSON
$json = json_encode($obj);
// overwrite existing file
// use 'w' mode to truncate file and open for writing
$fh = fopen($file_path, 'w');
// write JSON to file
$bytes_written = fwrite($fh, $json);
fclose($fh);
This assumes each of the JSON object repsentations in your original file are on a separate line.
I prefer this approach over string manipulation, as you can then have built in checks where you are decoding JSON to see if the input is valid JSON format that can be de-serialized. If the script operates successfully, this guarantees that your output will be able to be de-serialized by the caller to the script.
I am using flysystem with IRON IO queue and I am attempting to run a DB query that will be taking ~1.8 million records and while doing 5000 at at time. Here is the error message I am receiving with file sizes of 50+ MB:
PHP Fatal error: Allowed memory size of ########## bytes exhausted
Here are the steps I would like to take:
1) Get the data
2) Turn it into a CSV appropriate string (i.e. implode(',', $dataArray) . "\r\n")
3) Get the file from the server (in this case S3)
4) Read that files' contents and append this new string to it and re-write that content to the S3 file
Here is a brief run down of the code I have:
public function fire($job, $data)
{
// First set the headers and write the initial file to server
$this->filesystem->write($this->filename, implode(',', $this->setHeaders($parameters)) . "\r\n", [
'visibility' => 'public',
'mimetype' => 'text/csv',
]);
// Loop to get new sets of data
$offset = 0;
while ($this->exportResult) {
$this->exportResult = $this->getData($parameters, $offset);
if ($this->exportResult) {
$this->writeToFile($this->exportResult);
$offset += 5000;
}
}
}
private function writeToFile($contentToBeAdded = '')
{
$content = $this->filesystem->read($this->filename);
// Append new data
$content .= $contentToBeAdded;
$this->filesystem->update($this->filename, $content, [
'visibility' => 'public'
]);
}
I'm assuming this is NOT the most efficient? I am going off of these docs:
PHPLeague Flysystem
If anyone can point me in a more appropriate direction, that would be awesome!
Flysystem supports read/write/update stream
Please check latest API https://flysystem.thephpleague.com/api/
$stream = fopen('/path/to/database.backup', 'r+');
$filesystem->writeStream('backups/'.strftime('%G-%m-%d').'.backup', $stream);
// Using write you can also directly set the visibility
$filesystem->writeStream('backups/'.strftime('%G-%m-%d').'.backup', $stream, [
'visibility' => AdapterInterface::VISIBILITY_PRIVATE
]);
if (is_resource($stream)) {
fclose($stream);
}
// Or update a file with stream contents
$filesystem->updateStream('backups/'.strftime('%G-%m-%d').'.backup', $stream);
// Retrieve a read-stream
$stream = $filesystem->readStream('something/is/here.ext');
$contents = stream_get_contents($stream);
fclose($stream);
// Create or overwrite using a stream.
$putStream = tmpfile();
fwrite($putStream, $contents);
rewind($putStream);
$filesystem->putStream('somewhere/here.txt', $putStream);
if (is_resource($putStream)) {
fclose($putStream);
}
If you are working with S3, I would use the AWS SDK for PHP directly to solve this particular problem. Appending to a file is actually very easy using the SDK's S3 streamwrapper, and doesn't force you to read the entire file into memory.
$s3 = \Aws\S3\S3Client::factory($clientConfig);
$s3->registerStreamWrapper();
$appendHandle = fopen("s3://{$bucket}/{$key}", 'a');
fwrite($appendHandle, $data);
fclose($appendHandle);
I am in the process of trying to call a php script over http and receive a json object back from where I plan to process further.
Basically, the code is as follows:
<?php
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
$version=$_GET["v"];
$product=$_GET["p"];
$stream=$_GET["s"];
$cmd=$_GET["c"];
$string = file_get_contents("http://localhost:82/releasenote/src/getTSBDetails.php?p=$product&v=$version&s=$stream&c=$cmd");
print_r($string);
exit();
} else {
print("2");
$string = file_get_contents('tsbDetails.json');
}
When the get_file_contents http request is called directly in the browser, the output is a json, but when trying using the above there is no response.
<?php
// JSon request format is :
// {"userName":"654321#zzzz.com","password":"12345","emailProvider":"zzzz"}
// read JSon input
$data_back = json_decode(file_get_contents('php://input'));
// set json string to php variables
$userName = $data_back->{"userName"};
$password = $data_back->{"password"};
$emailProvider = $data_back->{"emailProvider"};
// create json response
$responses = array();
for ($i = 0; $i < 10; $i++) {
$responses[] = array("name" => $i, "email" => $userName . " " . $password . " " . $emailProvider);
}
// JSon response format is :
// [{"name":"eeee","email":"eee#zzzzz.com"},
// {"name":"aaaa","email":"aaaaa#zzzzz.com"},{"name":"cccc","email":"bbb#zzzzz.com"}]
// set header as json![enter image description here][2]
header("Content-type: application/json");
// send response
echo json_encode($responses);
?>
[1]: http://i.stack.imgur.com/I7imt.jpg
[2]: http://i.stack.imgur.com/XgvOT.jpg
First of all you should make sure your variables can be used in the url:
$version=urlencode($_GET["v"]);
$product=urlencode($_GET["p"]);
$stream=urlencode($_GET["s"]);
$cmd=urlencode($_GET["c"]);
Then you should check if the value you read in $string is valid json. You can use this answer for that.
Then, if your string contains valid json, you should just echo it.
Finally, if you always expect json from your script, you should also json_encode your error handling:
} else {
echo json_encode("2");
// $string = file_get_contents('tsbDetails.json'); /* commented out as you don't seem to use it */
}
i have problem with amazon s3 service and a web service writed with php.
This WS receive from $_POST a file base64encoded. I need to take this "string" and save to Amazon S3 bucket.
I didn't find a right solution for do that, and after a week of work I'm looking for help here.
//$file = 'kdK9IWUAAAAdaVRYdENvbW1lbnQAAAAAAENyZWF0ZWQgd'
$file = $_POST['some_file'];
$opt = array(
'fileUpload' => base64_decode($file),
'acl' => AmazonS3::ACL_PUBLIC
);
$s3 = new AmazonS3(AWS_KEY, AWS_SECRET_KEY);
$response = $s3->create_object($bucket, $filename, $opt);
Thanks
Per the docs, fileUpload expects a URL or path, or an fopen resource.
fileUpload - string|resource - Required; Conditional - The URL/path for the file to upload, or an open resource. Either this parameter or body is required.
You should pass the decoded file data via the body parameter instead:
body - string - Required; Conditional - The data to be stored in the object. Either this parameter or fileUpload must be specified.
The imagestring is the base64 encoded string that was passed from POST data. You can encode this image and make a file to anywhere - in my case /photos. I used this is a school server, but if Amazon server is similar enough, it could also work there.
$values = array();
foreach($_POST as $k => $v){
$values[$k] = $v;
}
$imagestring = $values['stringEncodedImage'];
$img = imagecreatefromstring(base64_decode($imagestring));
if($img != false)
{
echo 'making the file!';
imagejpeg($img, 'photos/'.$file_name);
}
Is there a way I can add a soap attachment to a request using PHP's built-in SoapClient classes? It doesn't look like it's supported, but maybe I can manually build the mime boundaries? I know the PEAR SOAP library supports them, but in order to use that I have to rewrite my entire library to use it.
Why don't you just send files using Data URI scheme rather than implement SoapAttachment ? Here is an example :
Client
$client = new SoapClient(null, array(
'location' => "http://localhost/lab/stackoverflow/a.php?h=none",
'uri' => "http://localhost/",
'trace' => 1
));
// Method 1 Array
// File to upload
$file = "golf3.png";
// First Example
$data = array();
$data['name'] = $file;
$data['data'] = getDataURI($file, "image/png");
echo "Example 1: ";
echo ($return = $client->upload($data)) ? "File Uploaded : $return bytes" : "Error Uploading Files";
// Method 2 Objects
// File to upload
$file = "original.png";
// Second Example
$attachment = new ImageObj($file);
$param = new SoapVar($attachment, SOAP_ENC_OBJECT, "ImageObj");
$param = new SoapParam($param, "param");
echo "Example 2: ";
echo ($return = $client->uploadObj($attachment)) ? "File Uploaded : $return bytes" : "Error Uploading Files";
Output
Example 1: File Uploaded : 976182 bytes
Example 2: File Uploaded : 233821 bytes
Server
class UploadService {
public function upload($args) {
$file = __DIR__ . "/test/" . $args['name'];
return file_put_contents($file, file_get_contents($args['data']));
}
public function uploadObj($args) {
$file = __DIR__ . "/test/" . $args->name;
$data = sprintf("data://%s;%s,%s", $args->mime, $args->encoding, $args->data);
return file_put_contents($file, file_get_contents($data));
}
}
try {
$server = new SOAPServer(NULL, array(
'uri' => 'http://localhost/'
));
$server->setClass('UploadService');
$server->handle();
} catch (SOAPFault $f) {
print $f->faultstring;
}
Client Util
// Function Used
function getDataURI($image, $mime = '') {
return 'data: ' . (function_exists('mime_content_type') ?
mime_content_type($image) : $mime) . ';base64,' .
base64_encode(file_get_contents($image));
}
// Simple Image Object
class ImageObj{
function __construct($file, $mime = "") {
$this->file = $file;
$this->name = basename($file);
if (function_exists('mime_content_type')) {
$this->mime = mime_content_type($file);
} elseif (function_exists('finfo_open')) {
$this->mime = finfo_file(finfo_open(FILEINFO_MIME_TYPE), $file);
} else {
$this->mime = $mime;
}
$this->encoding = "base64";
$this->data = base64_encode(file_get_contents($file));
}
}
Yes, you can build the MIME component of the message using something like imap_mail_compose.
You'll need to construct a multipart message as they do in the first example, putting the XML from the $request parameter, from an overridden SoapClient::__doRequest method, into the first part of the MIME message.
Then you can do as others have shown in the first imap_mail_compose example to add one or more messages parts with attachments. These attachements can, but do not have to be base64 encoded, they can just as well be binary. The encoding for each part is specified by part-specific headers.
You'll also need to cook up an appropriate set of HTTP headers, per the SwA Document #Baba linked to earlier.
Once it's all said and done, you should have something looking like the examples from that document:
MIME-Version: 1.0
Content-Type: Multipart/Related; boundary=MIME_boundary; type=text/xml;
start="<claim061400a.xml#claiming-it.com>"
Content-Description: This is the optional message description.
--MIME_boundary
Content-Type: text/xml; charset=UTF-8
Content-Transfer-Encoding: 8bit
Content-ID: <claim061400a.xml#claiming-it.com>
<?xml version='1.0' ?>
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Body>
..
<theSignedForm href="cid:claim061400a.tiff#claiming-it.com"/>
..
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
--MIME_boundary
Content-Type: image/tiff
Content-Transfer-Encoding: binary
Content-ID: <claim061400a.tiff#claiming-it.com>
...binary TIFF image...
--MIME_boundary--
And you can send that across the wire with the aforementioned overridden SoapClient::__doRequest method. Things I have noticed in trying to implement it myself thus far:
You may need to create an href URI reference from each SOAP node to the corresponding attachment, something like href="cid:claim061400a.tiff#claiming-it.com" above
You will need to extract the boundary component from the MIME content returned by imap_mail_compose for use in an HTTP Content-Type header
Don't forget the start component of the Content-Type header either, it should look something like this:
imap_mail_compose appears fairly minimal (but low hanging fruit), if it proves insufficient, consider Mail_Mime instead
Content-Type: Multipart/Related; boundary=MIME_boundary;
type=text/xml; start=""
Lastly, I'm not sure how evenly the various implementations of SwA are out there on the Internet... Suffice it to say, I've not been able to get an upload to a remote service with a crude implementation of what I've described above yet. It does seem like SwA is the typical SOAP attachment paradigm of choice though, from what I gather reading around on the net.