My goal is to store and retrieve raw data from a PHP script and client Swift code using HTTP POST. I am using MySQL database storing MEDIUMBLOB data.
I successfully sent the data from Swift and stored it into database.
Here is my working code used to store the data (I removed the WHERE clause to simplify), I can see the stored raw data using PHPMyAdmin (e.g. [BLOB - 345.6 KiB] which is the same size as client code data size) :
<?php
//...
$file = $_FILES['file']['tmp_name'];
$file_data = file_get_contents($file);
$file_info = finfo_open(FILEINFO_MIME_TYPE);
$file_type = finfo_file($file_info, $file);
$file_size = filesize($file);
$worldMap1 = $file_data;
$query = $query = "INSERT INTO " . $this->table_name . " SET worldMap1=:worldMap1";
//...
?>
Client Swift code (request structure) :
let boundary = generateBoundaryString()
var request = URLRequest(url: requestedUrl)
request.httpMethod = POSTMETHOD
request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
request.httpBody = createBodyWithParametersForData(
parameters: dataBody,
filePathKey: "file",
imageDataKey: data,
boundary: boundary
)
private func createBodyWithParametersForData(parameters: [String: Any]?, filePathKey: String?, imageDataKey: Data, boundary: String) -> Data {
var body = Data();
if parameters != nil {
for (key, value) in parameters! {
body.appendString(string: "--\(boundary)\r\n")
body.appendString(string: "Content-Disposition: form-data; name=\"\(key)\"\r\n\r\n")
body.appendString(string: "\(value)\r\n")
}
}
let filename = "file"
let mimetype = "application/octet-stream"
body.appendString(string: "--\(boundary)\r\n")
body.appendString(string: "Content-Disposition: form-data; name=\"\(filePathKey!)\"; filename=\"\(filename)\"\r\n")
body.appendString(string: "Content-Type: \(mimetype)\r\n\r\n")
body.append(imageDataKey)
body.appendString(string: "\r\n")
body.appendString(string: "--\(boundary)--\r\n")
return body
}
Now I simply want to send the data back to client Swift code (non working code) :
<?php
// required headers
header("Access-Control-Allow-Origin: *");
header("Content-Type: application/json; charset=UTF-8");
header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");
// Fetch the record:
$query = "SELECT worldMap1 FROM " . $this->table_name . "";
// prepare query
$stmt = $db->prepare($query);
//...
// execute query
$stmt->execute();
if ($stmt->rowCount() == 1) {
// Fetch the record into a variable:
$row = $stmt->fetch(PDO::FETCH_ASSOC);
$file_size = filesize($row["worldMap1"]);
// Send the content information:
header("Content-Type: application/octet-stream\n");
header("Content-Disposition: attachment; filename=\"file\"\n");
header("Content-Length: {$file_size}\n");
// Send the file:
echo $row["worldMap1"];
}
// Omit the closing PHP tag to avoid tainting the downloaded file
On client side, I either receive 0 byte data or timeout error codes.
I also tried to hardcode the Content-Length value e.g. 1000.
I do not see any clear documentation on how to send raw data from PHP code.
Here is the only useful information I found and used to store the data : http://www.peachpit.com/articles/article.aspx?p=1967015&seqNum=4
Generally, files are stored in a directory and not in a database.
It looks like the problem is most likely with your Content-Type: application/octet-stream\n header. I don't think it's enough to just tell the browser hey, I'm expecting an octet-stream.
In order for your browser to recognize this as an attachment properly I would change the header to indicate the actual type of file you are dealing with so it knows how to interpret the file.
Like so:
header('Content-Type: ' . $type);
Here are a list of accepted MIME-TYPES
You are also over-writing your your content type header from the "required" application/json to the octet-stream. I would probably not send out the aplication/json header. Even though I believe it would be over-written by the latter.
Make sure you send your headers out before ANYTHING is outputted by the script. Even an unknown white space can cause you problems.
I also do not see the need for the \n in you header.
I would also consider using single quotes instead of double quotes as your likely to make mistakes escaping your double quotes for your file name.
Hope that helps.
Again, if it were me I would not do this. I would upload all the files to a directory not a db.
Related
I'm unable to upload files using the Dio plugin and I can not figure out where's the problem. In Laravel the request is always empty.
What have I done so far:
Double check if the file path really exists using existsSync() function
Changed the Content-Type to application/x-www-form-urlencoded
Validated if the file is actually being uploaded - seems yes (?)
This is my flutter code:
File myFile = new File('/storage/emulated/0/Download/demo.docx');
FormData form = new FormData.from({
'title': 'Just testing',
'file': new UploadFileInfo(myFile, 'demo.docx')
});
Before sending through POST i checked if the file exists and returns true
print(myFile.existsSync());
And set the Content-type properly
Response response = await Dio().post(
myUrl,
data: form,
options: new Options(
contentType: ContentType.parse("application/x-www-form-urlencoded"),
),
);
Printing the result of the form returns
I/flutter (27929): ----dio-boundary-0118165894
I/flutter (27929): Content-Disposition: form-data; name="title"
I/flutter (27929): ----dio-boundary-1759467036
I/flutter (27929): Content-Disposition: form-data; name="file"; filename="demo.docx"
I/flutter (27929): Content-Type: application/octet-stream
Which I believe indicates that the file is being uploaded.
Now in laravel whenever i output the content received it always comes null the key file, but the key title comes with data.
The code print_r(json_encode($request->all())) retrieves
{"title":"Just testing","file":{}}
The same goes for print_r(json_encode($request->file('file'))).
What am i missing?
Solved.
This took me a while to figure it out, but i end up realizing there's two problems with this approach:
Laravel $request is empty, but $_FILES is not
Sending multiple files can not be sent using arrays as the documentation tells
So, in order to achieve my goal which allows the user to select multiple files dynamically and upload them at the same time, here's the logic behind:
Flutter
The form must be created without setting the files right away:
FormData form = new FormData.from(
{
'title': 'Just testing',
});
Since the function .from is a Map<String, dynamic> values can be added after.
/*
* files = List<String> containing all the file paths
*
* It will end up like this:
* file_1 => $_FILES
* file_2 => $_FILES
* file_3 => $_FILES
*/
for (int i = 0; i < files.length; i++) {
form.add('file_' + i.toString(),
new UploadFileInfo(new File(files[i]), files[i].toString()));
}
There is no need to set up a different Content-Type, therefore this is enough:
Response response = await Dio().post(myUrl, data: form);
Laravel / PHP
Forget about accessing the file through $request->file() and instead use the old school approach.
$totalFiles = count($_FILES);
for ($i = 0; $i < $totalFiles; $i++)
{
$file = $_FILES['file_' . $i];
// handle the file normally ...
$fileName = basename($file['name']);
$fileInfo = pathinfo($file);
$fileExtension = $fileInfo['extension'];
move_uploaded_file($file['tmp_name'], $path);
}
I know this is an old post but this may help someone.
this solution works for me, upload multi-file to server use Flutter Dio library and Laravel as backend. correct me if I did it wrong.
Flutter
BaseOptions _dioOption({#required String token}) {
BaseOptions options = new BaseOptions(baseUrl: baseUrl, headers: {
Headers.acceptHeader: Headers.jsonContentType,
Headers.contentTypeHeader: Headers.jsonContentType,
"Authorization": "Bearer $token"
});
return options;
}
dioPostProduct( {#required ProductToUpload productToUpload,
#required String url, String token}) async {
//productToUpload.images is a List<File>
List<Object> filesData = new List<Object>();
for (final file in productToUpload.images) {
filesData.add(MultipartFile.fromFileSync(file.path,
filename: file.path.split('/').last));
}
FormData data = FormData.fromMap({
"subcategory_id": productToUpload.subcategory_id,
"name": productToUpload.name,
"detail": productToUpload.detail,
"price": productToUpload.price,
"condition_id": productToUpload.condition_id,
"images": filesData,
});
Dio dio = new Dio(_dioOption(token: token));
Response response;
response = await dio.post(url, data: data);
if (response.statusCode == 200) {
print(response.data);
}
}
Laravel
For php resize image I use library intervention
$images = Collection::wrap(request()->file('images'));
$directory = '/product_images'; //make sure directory is exist
foreach ($images as $image) {
$basename = Str::random();
$original = $basename . '.' . $image->getClientOriginalExtension();
$thumbnail = $basename . '_thumb.' . $image->getClientOriginalExtension();
Image::make($image)
->fit(400, 400)
->save(public_path($directory . '/' . $thumbnail));
$image->move(public_path($directory), $original);
}
I am using v4l2 library on linux, take a picture and want to send it to a php server via c program.
I want to using a socket to do it. But i don't know how to pass the image to request .
This is my sample code:
int portno = 80;
struct sockaddr_in serv_addr;
int sockfd, bytes, sent, received, total;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
char message[1024],response[4096];
if (sockfd < 0){
printf("ERROR opening socket");
}
memset(&serv_addr,0,sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(portno);
if(inet_pton(AF_INET, CONST_DOMAIN, &serv_addr.sin_addr)<=0){
printf("\n inet_pton error occured\n");
return 1;
}
if (connect(sockfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr)) < 0) {
printf("ERROR connecting");
}
char content[1024];
char *contentTemp="image_name=%s";
sprintf(content,contentTemp,imageName);
char *headerTemp="POST %supload.php HTTP/1.0\r\nHost: %s\r\nContent-Type: application/x-www-form-urlencoded\r\nContent-length: %d\r\n\r\n%s";
sprintf(message,headerTemp,SERVICE_PATH,SERVICE_HOST,strlen(content),content);
write(sockfd,message,strlen(message));
Can i using this way to post an image to server (include its name) ?
Any suggest for me ?
Thanks
PS: sorry about my english skill.
You are including only file name. You have to include the whole image file contents into post data stream. Forms submitting binary data with POST request should use multipart/form-data content type. You can't use application/x-www-form-urlencoded type.
From HTML 4.01 specification:
The content type "application/x-www-form-urlencoded" is inefficient
for sending large quantities of binary data or text containing
non-ASCII characters. The content type "multipart/form-data" should be
used for submitting forms that contain files, non-ASCII data, and
binary data.
You could adjust your code like this:
char *filename="file.jpg"; // this example uses jpeg
// optionally load file from filesystem
// though I think you have it in a buffer, don't you?
FILE *file = fopen(filename, "rb");
char binary[1024]; // adjust buffer size to your needs
size_t filesize = fread(binary, 1, sizeof(binary), file);
// check for error here to make sure read succeeded
fclose(file);
// multipart/form-data POST header
const char *headerTemp = "POST %supload.php HTTP/1.0\r\n"
"Host: %s\r\n"
"Content-Type: multipart/form-data; boundary=BoUnDaRy\r\n"
"Content-Length: %lu\r\n"
"\r\n";
// first and only part beginning
const char *bodyTemp =
"--BoUnDaRy\r\n"
"Content-Disposition: form-data; name=\"file\"; filename=\"%s\"\r\n"
"Content-Type: image/jpeg\r\n"
"Content-Transfer-Encoding: binary\r\n"
"\r\n";
// and ending
const char body2[] = "\r\n"
"--BoUnDaRy--\r\n";
char body1[1024]; // adjust buffer size to your needs
// calculate body size, will be included in Content-Length header
size_t body_size = strlen(body1) + strlen(body2) + filesize;
snprintf(header, 1024, headerTemp, SERVICE_PATH, SERVICE_HOST, body_size);
snprintf(body1, 1024, bodyTemp, filename);
// you should add checking for each write return value
write(sockfd, header, strlen(header));
write(sockfd, body1, strlen(body1));
write(sockfd, binary, filesize);
write(sockfd, body2, strlen(body2));
After sending data you should read server response, for example:
while (1) {
ssize_t result = recv(sockfd, response, sizeof(response), 0);
if (result == 0) {
break;
} else if (result < 0) {
perror("reading socket failed");
break;
}
printf("%s\n", response);
}
close(sockfd);
If you just close socket without waiting for the response server may complain and return error. You should also check if the response confirms valid request.
How can I POST parameters with a file object to a URL using httplib in python web services.
Am using the following scripts:
import httplib
import urllib
params = urllib.urlencode({"#str1":"string1", "#str2":"string2", "#file":"/local/path/to/file/in/client/machine", "#action":"action.php" })
headers = {"Content-type":"application/pdf , text/*" }
conn = httplib.HTTPConnection("192.168.2.17")
conn.request("POST", "/SomeName/action.php", params, headers)
response = conn.getresponse()
print response.status, response.reason
data = response.read()
data
conn.close()
And I have the following output:
200
OK
<html>.....some html code </html>
I wrote some php code for save those string and the file in DB
My problem is that, Am only getting the file path as a sting but not my file.
May be I have to send the file object like,
file_obj = open("filename.txt","r")
conn.request("POST", "/SomeName/action.php", file_obj, headers)
But I want to send both strings and file. Any suggestions to solve this?
EDIT
I change my code as follows:
When i send a pdf file, by directly using httplib, to my server the file saves as BIN file.
def document_management_service(self,str_loc_file_path,*args):
locfile = open(str_loc_file_path,'rb').read()
host = "some.hostname.com"
selector = "/api/?task=create"
fields = [('docName','INVOICE'),('docNumber','DOC/3'),('cusName','name'),('cusNumber','C124'),('category','INVOICE'),('data','TIJO MRS,SOME/DATA/CONTENT,Blahblah,2584.00,blahblah'),('action','create')]
files = [('strfile','File.pdf',locfile)]
response = self.post_multipart(host, selector, fields, files)
print response
pass
def post_multipart(self,host, selector, fields, files):
content_type, body = self.encode_multipart_formdata(fields, files)
h = httplib.HTTP(host)
h.set_debuglevel(1)
h.putrequest('POST', selector)
h.putheader('content-type', content_type)
h.putheader('content-length', str(len(body)))
h.putheader('Host', host)
h.endheaders()
h.send(body)
errcode, errmsg, headers= h.getreply()
return h.file.read()
def encode_multipart_formdata(self, fields, files):
LIMIT = '----------lImIt_of_THE_fIle_eW_$'
CRLF = '\r\n'
L = []
for (key, value) in fields:
L.append('--' + LIMIT)
L.append('Content-Disposition: form-data; name="%s"' % key)
L.append('')
L.append(value)
for (key, filename, value) in files:
L.append('--' + LIMIT)
L.append('Content-Disposition: form-data; name="%s"; filename="%s"' % (key, filename))
L.append('Content-Type: %s' % self.get_content_type(filename))
L.append('')
L.append(value)
L.append('--' + LIMIT + '--')
L.append('')
body = CRLF.join(L)
content_type = 'multipart/form-data; boundary=%s' % LIMIT
return content_type, body
def get_content_type(self, filename):
return mimetypes.guess_type(filename)[0] or 'application/octet-stream'
I have debug the request which shows as:
[('content-length', '4191'), ('accept-ranges', 'bytes'), ('server', 'Apache/2.2.12 (Ubuntu)'), ('last-modified', 'Tue, 23 Oct 2012 04:46:36 GMT'), ('etag', 'W/"567dd-105f-4ccb2a7a9a500"'), ('date', 'Tue, 23 Oct 2012 04:46:36 GMT'), ('content-type', 'application/pdf')]
multipart/form-data; boundary=----------lImIt_of_THE_fIle_eW_$
And I didn't try requests,Coz I would like to solve this with httplib(without any external lib)
To post parameters and a file in a body you could use multipart/form-data content type:
#!/usr/bin/env python
import requests # $ pip install requests
file = 'file content as a file object or string'
r = requests.post('http://example.com/SomeName/action.php',
files={'file': ('filename.txt', file)},
data={'str1': 'string1', 'str2': 'string2'})
print(r.text) # response
requests.post sends to the server something like-this:
POST /SomeName/action.php HTTP/1.1
Host: example.com
Content-Length: 449
Content-Type: multipart/form-data; boundary=f27f8ef67cac403aaaf433f83742bd64
Accept-Encoding: identity, deflate, compress, gzip
Accept: */*
--f27f8ef67cac403aaaf433f83742bd64
Content-Disposition: form-data; name="str2"
Content-Type: text/plain
string2
--f27f8ef67cac403aaaf433f83742bd64
Content-Disposition: form-data; name="str1"
Content-Type: text/plain
string1
--f27f8ef67cac403aaaf433f83742bd64
Content-Disposition: form-data; name="file"; filename="filename.txt"
Content-Type: text/plain
file content as a file object or string
--f27f8ef67cac403aaaf433f83742bd64--
To reproduce it with httplib see POST form-data with Python example.
A simpler solution if your parameters do not contain much data is to pass them in the url query part and leave the body to contain only the file:
#!/usr/bin/env python
import urllib
import requests # $ pip install requests
params = {'str1': 'string1', 'str2': 'string2', 'filename': 'filename.txt'}
file = 'file content as a file object or string, etc'
url = 'http://example.com/SomeName/action.php?' + urllib.urlencode(params)
r = requests.post(url, data=file, headers={'Content-Type': 'text/plain'})
print(r.text) # response
It corresponds to the following HTTP request:
POST /SomeName/action.php?str2=string2&str1=string1&filename=filename.txt HTTP/1.1
Host: example.com
Content-Length: 39
Content-Type: text/plain
Accept-Encoding: identity, deflate, compress, gzip
Accept: */*
file content as a file object or string
It should be easier to translate to httplib if you need it.
The following code can also solve the problem with transfering file with other meta data using httplib (with out any external libraries):
def document_management_service_success(self,str_loc_file_path,*args):
locfile = open(str_loc_file_path,'rb').read()
str_loc_file = locfile.split('#end_pymotw_header')
initial_data = str_loc_file[0]
encoded_data = ''.join("{0:08b}".format(ord(c)) for c in initial_data)
params = urllib.urlencode({'docName':'INVOICE', 'docNumber':'RF/2', 'cusName':'Tijo john', 'cusNumber':'C124', 'category':'INVOICE', 'data':encoded_data})
headers = {"Accept": "Text/*","Content-Disposition":"form-data" ,"Content-Type": "application/x-www-form-urlencoded, application/pdf, form-data"}
conn = httplib.HTTPConnection("efiling.nucoreindia.com")
conn.connect()
conn.set_debuglevel(1)
conn.request("POST", "/api/?task=create", params, headers)
response = conn.getresponse()
print "Server Response status is"+str(response.status)+"and Reason is,"+str(response.reason)
print response.getheaders()
print response.read()
pass
I'm trying to upload an image file to a PHP file on a web server.
On VB.NET ->
My.Computer.Network.UploadFile(tempImageLocation, "website.com/upload.php")
tempImageLocation is a location on the harddrive where the image is located. The image is located on the harddrive where I specify it.
On PHP ->
$image = $_FILES['uploads']['name'];
I don't understand, because it is loading the page - but PHP can't find the file under 'uploads'
Google brought me here while I was searching for the same question. Thanks people it gave me the idea, and with a little knowledge of PHP, I've achieved it. I know its an old question but still I'm going to share my code so it could help people in future..
VB:
My.Computer.Network.UploadFile("e:\file1.jpg", "http://www.mysite.com/upl/upl.php")
PHP:
move_uploaded_file($_FILES["file"]["tmp_name"], "upload/" . $_FILES["file"]["name"]);
and don't forget to give the upload folder the appropriate permissions.
I know, is old.. but here is solution work to me:
Private Sub HttpUploadFile(
ByVal uri As String,
ByVal filePath As String,
ByVal fileParameterName As String,
ByVal contentType As String)
Dim myFile As New FileInfo(filePath)
Dim sizeInBytes As Long = myFile.Length
Dim boundary As String = "---------------------------" & DateTime.Now.Ticks.ToString("x")
Dim newLine As String = System.Environment.NewLine
Dim boundaryBytes As Byte() = Encoding.ASCII.GetBytes(newLine & "--" & boundary & newLine)
Dim request As Net.HttpWebRequest = Net.WebRequest.Create(uri)
request.ContentType = "multipart/form-data; boundary=" & boundary
request.Method = "POST"
request.KeepAlive = True
'request.Credentials = Net.CredentialCache.DefaultCredentials
Using requestStream As IO.Stream = request.GetRequestStream()
Dim formDataTemplate As String = "Content-Disposition: form-data; name=""{0}""{1}{1}{2}"
requestStream.Write(boundaryBytes, 0, boundaryBytes.Length)
Dim headerTemplate As String = "Content-Disposition: form-data; name=""{0}""; filename=""{1}""{2}Content-Type: {3};"
Dim header As String = String.Format(headerTemplate, fileParameterName, filePath, newLine, contentType)
header = header & vbNewLine & "Content-Length: " & sizeInBytes.ToString & vbNewLine
header = header & "Expect: 100-continue" & vbNewLine & vbNewLine
'MsgBox(header)
Debug.Print(header)
Dim headerBytes As Byte() = Encoding.UTF8.GetBytes(header)
requestStream.Write(headerBytes, 0, header.Length)
Using fileStream As New IO.FileStream(filePath, IO.FileMode.Open, IO.FileAccess.Read)
Dim buffer(4096) As Byte
Dim bytesRead As Int32 = fileStream.Read(buffer, 0, buffer.Length)
Do While (bytesRead > 0)
requestStream.Write(buffer, 0, bytesRead)
bytesRead = fileStream.Read(buffer, 0, buffer.Length)
Loop
End Using
Dim trailer As Byte() = Encoding.ASCII.GetBytes(newLine & "--" + boundary + "--" & newLine)
requestStream.Write(trailer, 0, trailer.Length)
requestStream.Close()
End Using
Dim response As Net.WebResponse = Nothing
Try
response = request.GetResponse()
Using responseStream As IO.Stream = response.GetResponseStream()
Using responseReader As New IO.StreamReader(responseStream)
Dim responseText = responseReader.ReadToEnd()
Debug.Print(responseText)
End Using
End Using
Catch exception As Net.WebException
response = exception.Response
If (response IsNot Nothing) Then
Using reader As New IO.StreamReader(response.GetResponseStream())
Dim responseText = reader.ReadToEnd()
Diagnostics.Debug.Write(responseText)
End Using
response.Close()
End If
Finally
request = Nothing
End Try
End Sub
Using:
HttpUploadFile("https://www.yousite.com/ws/upload.php?option1=sss&options2=12121", FULL_FILE_NAME_PATH_IN_YOUR_PC, "files", "multipart/form-data")
I copy somen code in a website i dont remember.
I only put this 2 lines of code to work:
header = header & vbNewLine & "Content-Length: " & sizeInBytes.ToString & vbNewLine
header = header & vbNewLine & "Expect: 100-continue" & vbNewLine
hope help.
Here is the complete example for uploading file using Visual Basic and on Server Side PHP (Rest API) GitHub Link
Here is quick and dirty tutorial for you: PHP File Upload
'uploads' is just name attribute value of element of a form:
<input type="file" name="uploads" />
or in other words, this is POST variable name that is accessed over $_FILES global.
If you don't set the field name, you can save the uploaded file with this
$file = array_shift($_FILES);
move_uploaded_file($file['tmp_name'], '/path/to/new/location/'.$file['name']);
Take a look at some of these other answers. PHP requires files uploaded with the POST method to use certain headers which are normally set by the browser when uploading from a web form but which can be set in VB with the HttpWebRequest Class.
As for the PHP side, you aren't going to be able to locate the file immediately after uploading with $image = $_FILES['uploads']['name'];. PHP stores uploads with a temporary filename accessible with the $_FILES['uploads']['tmp_name'] variable, and using move_uploaded_file() is the standard way of shifting uploads from temporary storage into a permanent uploads directory. The PHP manual provides a good overview of that.
Here's my sample server php file:
<?php
// write to a log file so you know it's working
$msg = $_POST['w'];
$logfile= 'data.txt';
$fp = fopen($logfile, "a");
fwrite($fp, $msg);
fclose($fp);
$file = array_shift($_FILES);
move_uploaded_file($file['tmp_name'], '/MAMP/htdocs/test/'.$file['name']);
?>
Here's the code to call #Rodrigo's code:
HttpUploadFile("http://localhost/test/test.php?w=hello&options2=12121", "C:\temp\bahamas.mp3", "files", "multipart/form-data")
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.