I have an app that uploads some data to a MySQL database via some PHP web services I wrote. I have got the text data to upload successfully, but I am struggling trying to get an image to upload. Basically, the user takes a photo using their iPhone camera, I convert it to a UIImage and now want to upload it.
I have looked at the following sources for help but they haven't seemed to work for me:
How to Upload text, pdf, doc, zip file via PHP file upload to MySQL
https://www.youtube.com/watch?v=HqxeyS961Uk (The code in here wouldn't compile for me and I copied it letter for letter)
How to post a picture from iPhone app (in Swift) to MySQL database?
Saving image on db mysql through swift
Swift - uploading base64 encoded image to php and displaying the image
Getting a UIImage from MySQL using PHP and jSON
I want to upload the image and text together in the same query if possible, as they relate to one another and my MySQL table assigns an auto number for new rows.
Edit
Thanks to the answers below. I am a little unsure as to how to implement the changes you guys suggested. My Swift code at the moment to upload text data is:
let url:NSURL = NSURL(string: "http://www.website.com.au/savenewexpense.php")!
let request:NSMutableURLRequest = NSMutableURLRequest(URL: url)
request.HTTPMethod = "POST"
var dataDictionary:[String:AnyObject] = ["Date" : strDate, "Staff" : strStaff, "Amount" : dblAmount, "Category" : strCategory]
var data:NSData = NSJSONSerialization.dataWithJSONObject(dataDictionary, options: nil, error: nil)!
request.HTTPBody = data
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
let task:NSURLSessionDataTask = NSURLSession.sharedSession().dataTaskWithRequest(request, completionHandler: { (data:NSData!, response:NSURLResponse!, error:NSError!) -> Void in
let response:[String:String] = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: nil) as! [String:String]
if let result = response["result"] {
if result == "success" {
NSLog("Text Data saved successfully")
if self.SendImageToServer() == true {
NSLog("Image Data saved successfully")
self.SaveSuccessful()
} else {
NSLog("Image Data failed to save")
self.DataFail()
}
} else {
NSLog("Error saving data")
self.DataFail()
}
} else {
NSLog("No response")
self.NoResponse()
}
})
task.resume()
}
and my PHP code is:
// Create connection
$conn = new mysqli($servername, $username, $password, $dbname);
// Check connection
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
$json = file_get_contents('php://input');
$data = json_decode($json, true);
$stmt = $conn->prepare("INSERT INTO tblExpenses (Date, Staff, Amount, Category) VALUES (?, ?, ?, ?)");
$txtDate = $data['Date'];
$txtStaff = $data['Staff'];
$txtAmount = $data['Amount'];
$txtCategory = $data['Category'];
$stmt->bind_param("ssds", $txtDate, $txtStaff, $txtAmount, $txtCategory);
$stmt->execute();
echo '{"result" : "success"}';
$stmt->close();
$conn->close();
How should I implement the image into this?
Thanks
What you have to do is set the Boundary for the Image as an example use the code to Convert the NSDATA of image to the boundary values
if let dta = data as? NSData {
bodyData.appendData(String(format:"\r\n--\(boundary)\r\n").dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!);
bodyData.appendData(String(format:"Content-Disposition: form-data; name=\"photo\(i)\"; filename=\"image.png\"\r\n").dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!);
bodyData.appendData(String(format:"Content-Type: image/png\r\n\r\n").dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!);
bodyData.appendData(dta);
}
Here what you do is you set the image name and then the Image Data first you convert the data using allowLossyConversion and then send the POST data with the other data to service
request.HTTPBody = bodyData
just use this to send the image
Related
func convertImageToBase64(image: UIImage) -> String {
let imageData = UIImagePNGRepresentation(image)
let base64String = imageData?.base64EncodedString(options:.lineLength64Characters)
return base64String!
}// end convertImageToBase64
Swift code is here (base 64 encode part) :
let base64string = self.convertImageToBase64(image: self.pro_images.image!)
let imgbase64str = base64string.replacingOccurrences(of:"+", with: "%2B")
request.httpMethod = "POST"
let postString = ("email=\(savedValue!)&image=\(imgbase64str)&subject=\(self.analy_title.text!)&content=\(self.analy_text.text!)")
request.httpBody = postString.data(using: .utf8)
let task = URLSession.shared.dataTask(with: request) { data, ....
php code is here :
if($image != "null"){
if(file_put_contents($upload_path,base64_decode($image))){
I want to upload my base64 encode image
but I can't got original picture.
because picture is not display.
How to solve this problem ?
I am trying to send an image to server, but the image should be in Base64 format. I am using this function to send it:
func upload_signature_staff(ticket: NSString){
let defaults = NSUserDefaults.standardUserDefaults()
let stringOne = defaults.stringForKey(defaultsKeys.staff_id)
let stringtwo = defaults.stringForKey(defaultsKeys.mst_customer)
let sig = defaults.stringForKey("staff_signature")
let request = NSMutableURLRequest(URL: NSURL(string: "http://xxxxxxxxxxxxx/upload.php")!)
request.HTTPMethod = "POST"
let postString = "action=add_signature&mst_customer=\((stringtwo!))&ticket=\((ticket))&signature=\((sig!))¤t_user=\((stringOne!))&item_type=10"
print(postString)
request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding)
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, response, error in
guard error == nil && data != nil else { // check for fundamental networking error
print("error=\(error)")
return
}
if let httpStatus = response as? NSHTTPURLResponse where httpStatus.statusCode != 200 { // check for http errors
print("statusCode should be 200, but is \(httpStatus.statusCode)")
print("response = \(response)")
}
let responseString = NSString(data: data!, encoding: NSUTF8StringEncoding)
print("responseString = \(responseString)")
}
task.resume()
//self.performSegueWithIdentifier("goto_main2", sender: self)
}
The sig variable holds the Base64 string, it is being printed in my console so I can verify that the string is correct. I am also printing the postString and upon inspection it is also correct that the signature is matching the Base64 String. But when I open phpmyadmin, I see the field of my image with incomplete Base64 string, maybe 1/4 is just there.
Here's my php code, in case you want to see it:
<?php
require_once("../c.php");
$action = trim($_POST['action']);
if($action == "add_signature"){
$mst_customer = trim($_POST['mst_customer']);
$ticket = trim($_POST['ticket']);
$signature = trim($_POST['signature']);
$current_user = trim($_POST['current_user']);
$item_type = trim($_POST['item_type']);
$inputtime = time();
$sql = "INSERT INTO ticket_items SET mst_customer = '$mst_customer', ticket = '$ticket', ticket_item_type = '$item_type', details = '$signature', addedby = '$current_user', lastupdate = '$inputtime' ";
mysql_query($sql) or die(mysql_error());
}
?>
I think this was solved in the comments but here's a recap:
Inserting base64 strings that were too long (variable length?) in a varchar(255) field resulted in missing data. As far as I can tell, increasing the size of the field solved the immediate problem. I use the term "immediate" because, as #YvesLeBorg pointed out in the comments, this is bound to fail at some point without input size restrictions on the backend.
Additionally, I couldn't ignore the fact that the PHP/SQL code was wide open to injections.
Passing $mst_customer = trim($_POST['mst_customer']); on to "INSERT INTO ticket_items SET mst_customer = '$mst_customer' and then executing via mysql_query($sql) or die(mysql_error()); is dangerous!
Anybody could write anything in the $_POST parameter and SQL would happily accept it. Prepared statements, PDO, input sanitization etc. are there for a reason.
Finally, there was an issue concerning vanishing + signs in the base64 data. This was the result of missing url encoding of the post data.
I think that sums it up.
The goal is to display a result of json_encode result from php into swift as a label. I have referenced to many tutorials and practices of parsing json with swift, but I still cant understand the issue.
I referenced to an example that uses date.jsontest.com and that works fine.
In my project, I have parsed this simple JSON object to start:
[{"username":"user"}]
Here is the swift code:
override func viewDidLoad() {
super.viewDidLoad()
// 1
//let urlAsString = "http://date.jsontest.com"
let urlAsString = "http://(ip address)/service.php"
let url = NSURL(string: urlAsString)!
let urlSession = NSURLSession.sharedSession()
// 2
let jsonQuery = urlSession.dataTaskWithURL(url, completionHandler: { data, response, error -> Void in
if (error != nil) {
println(error.localizedDescription)
}
var err: NSError?
// 3
var jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &err) as NSDictionary
if (err != nil) {
println("JSON Error \(err!.localizedDescription)")
}
// 4
//let jsonDate: String! = jsonResult["date"] as NSString
//let jsonId = jsonResult["id"] as NSString
//let jsonTime: String! = jsonResult["time"] as NSString
let jsonUser: String! = jsonResult[0] as NSString
dispatch_async(dispatch_get_main_queue(), {
//self.dateLabel.text = jsonId
//self.timeLabel.text = jsonUser
self.dateLabel.text = jsonUser
})
})
// 5
jsonQuery.resume()
//searchItunesFor("JQ Software")
//startConnection()
// Do any additional setup after loading the view, typically from a nib.
}
The code runs fine when I use http://date.jsontest.com as the string along with jsonDate and jsonTime.
I tried to understand the debugger in Xcode, but as a new user to Swift, its hard to understand what the issue is. All I could root out is that there is an issue with how my JSON is formatted.
Any tips are greatly appreciated, thanks.
EDIT:
Here is the code http://(ip address)/service.php that gives me the JSON
<?php
//connect to MySQL
// Create connection
$con=mysqli_connect($hostname,$username,$password,$database);
// Check connection
if (mysqli_connect_errno())
{
echo "Failed to connect to MySQL: " . mysqli_connect_error();
}
// This SQL statement selects ALL from the table 'Locations'
$sql = "SELECT username FROM members WHERE id = 1";
// Check if there are results
if ($result = mysqli_query($con, $sql))
{
// If so, then create a results array and a temporary one
// to hold the data
$resultArray = array();
$tempArray = array();
// Loop through each row in the result set
while($row = $result->fetch_object())
{
// Add each row into our results array
$tempArray = $row;
array_push($resultArray, $tempArray);
}
// Finally, encode the array to JSON and output the results
echo json_encode($resultArray);
}
// Close connections
mysqli_close($con);
?>
I'm using PHP & JSON to extract some data from a database.
This is my PHP file
<?php
error_reporting(0);
ini_set('error_reporting', E_ALL);
ini_set('display_errors','Off');
$mysqli = new mysqli("localhost", "root", $_REQUEST['password'], "");
if ($mysqli->connect_errno) {
echo "Failed to connect to DB.";
die();
} else {
$dbs = array();
$res = $mysqli->query("SHOW DATABASES");
$res->data_seek(0);
if ($res->num_rows > 0) {
while($row = $res->fetch_assoc()) {
$dbs[] = $row;
}
echo json_encode($dbs);
} else {
echo "Failed to get list of databases from server.";
die();
}} ?>
If the password is wrong, then the system outputs "Failed to connect to DB"
In my program, I have things to handle errors, but I am stuck at one part.
let urlString = "http://\(hostTextField.text):\(portTextField.text)/dblist.php? &password=\(passTextField.text)"
let url: NSURL = NSURL(string: urlString)!
let urlSession = NSURLSession.sharedSession()
println(url)
println(urlSession)
//2
let jsonQuery = urlSession.dataTaskWithURL(url, completionHandler: { data, response, error -> Void in
println(response)
println(data)
if (error != nil) {
println("Can't connect using credentials")
dispatch_async(dispatch_get_main_queue(), {
HUDController.sharedController.hide(afterDelay: 0.1)
})
sleep(1)
var refreshAlert = UIAlertController(title: "Camaleon Reports", message: "Can't connect to the database", preferredStyle: UIAlertControllerStyle.Alert)
refreshAlert.addAction(UIAlertAction(title: "Retry", style: .Default, handler: { (action: UIAlertAction!) in
println("Yes Logic")
}))
self.presentViewController(refreshAlert, animated: true, completion: nil)
return }
var err: NSError?
var jsonResult: [Dictionary<String, String>] = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &err) as [Dictionary<String, String>]
// 3
if (err != nil) {
println("Still cant connect....")
println("JSON Error \(err!.localizedDescription)")
}
var jsonDB : [Dictionary<String, String>] = jsonResult
for currentDictionary in jsonDB{
var currentEntry = currentDictionary["Database"] as String!
My program crashes if I don't have the right password, but have the right IP address and Port/User for the MYSQL Database.
It crashes with this:
fatal error: unexpectedly found nil while unwrapping an Optional value
and points towards jsonResult. It makes sense, cause I don't retrieve two strings.
My problem is that if my password is off, then my PHP file echoes a string. How can I search for that string so that I can use an if statement and stop my application from crashing?
Your problem is likely in this line (wrapped for clarity):
var jsonResult: [Dictionary<String, String>] =
NSJSONSerialization.JSONObjectWithData(data,
options: NSJSONReadingOptions.MutableContainers,
error: &err) as [Dictionary<String, String>]
When your PHP script reports the error by just returning the string, it has returned invalid JSON. When you use NSJSONSerialization.JSONObjectWithData to parse it, that method will return nil if the JSON is invalid, as yours is.
You then take that value and assign it to a Swift variable that you've declared is not an optional. Trying to assign nil to a variable not declared with either ? or ! is a runtime error in Swift. (You don't get an error at compile time because you're using as to cast the value.)
One way to fix this would be to change your PHP so the error is proper JSON:
echo "{ \"error\": \"Failed to connect to DB.\" }"; # or something, my PHP is rusty
But that still leaves your Swift program in a fragile state; getting anything but proper JSON back from the server will make it crash.
Better is to declare the jsonResult variable as being an optional:
var jsonResult: [Dictionary<String, String>]? =
NSJSONSerialization.JSONObjectWithData(data,
options: NSJSONReadingOptions.MutableContainers,
error: &err) as [Dictionary<String, String>]?
Then in your code you can explicitly check whether jsonResult is nil, and if it is, you know an error has occurred, and can go back and look at the data object to see what it was.
Even that, though, can leave you in trouble. The root of a JSON document doesn't have to be a dictionary; it could be an array. And even if it is a dictionary, the values may not all be strings; they could be numbers, booleans, or nested arrays or dictionaries!
Objective-C's relatively lax type checking makes this easy to deal with, but Swift is stricter. Best might be to use one of the Swift-specific JSON libraries. That'll make your code far more robust.
Good luck!
There are two issues. One is the PHP and one is the Swift.
Your PHP really should never just report an error message. I'd suggest that it always return JSON. This will make it easier for your client code to detect and handle errors appropriately.
<?php
header("Content-Type: application/json");
$response = array();
error_reporting(0);
ini_set('error_reporting', E_ALL);
ini_set('display_errors','Off');
if (!isset($_REQUEST['password'])) {
$response["success"] = false;
$response["error_code"] = 1;
$response["error_message"] = "No password provided";
echo json_encode($response);
exit();
}
$mysqli = new mysqli("localhost", "root", $_REQUEST['password'], "");
if ($mysqli->connect_errno) {
$response["success"] = false;
$response["error_code"] = 2;
$response["mysql_error_code"] = $mysqli->connect_errno;
$response["error_message"] = $mysqli->connect_error;
echo json_encode($response);
exit();
}
if ($res = $mysqli->query("SHOW DATABASES")) {
$dbs = array();
$res->data_seek(0);
if ($res->num_rows > 0) {
while($row = $res->fetch_assoc()) {
$dbs[] = $row;
}
$response["success"] = true;
$response["results"] = $dbs;
} else {
$response["success"] = false;
$response["error_code"] = 3;
$response["error_message"] = "Failed to get list of databases from server.";
}
$res->close();
} else {
$response["success"] = false;
$response["error_code"] = 4;
$response["mysql_error_code"] = $mysqli->errno;
$response["error_message"] = $mysqli->error;
}
$mysqli->close();
echo json_encode($response);
?>
Note, this:
Specifies application/json header for Content-Type;
Always returns a dictionary, containing
a "success" key, which is either true or false;
if an error, an error_code indicating the type of error (1 = no password provided; 2 = connect failed; 3 = no databases found; 4 = some SQL error);
if an error, an error_msg string indicating the error message string; and
if a success, a results array (much like you used to return at the root level).
On the Swift side, you need to :
Change it to look for these various server app-level errors (note, I make the top level structure a dictionary, and your original array of dictionaries a particular value;
You might want to proactively check the statusCode of the response object, to make sure the server gave you a 200 return code (e.g. 404 means that the page was not found, etc.);
You might also want to check for JSON parsing errors (in case some bug in the server prevented well-formed JSON from being returned); and
You really should be percent-escaping the password (because if it included + or & characters, it wouldn't get transmitted successfully otherwise).
Thus, you might have something like:
let encodedPassword = password.stringByAddingPercentEncodingForURLQueryValue()!
let body = "password=\(encodedPassword)"
let request = NSMutableURLRequest(URL: URL!)
request.HTTPBody = body.dataUsingEncoding(NSUTF8StringEncoding)!
request.HTTPMethod = "POST"
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, response, error in
// detect fundamental network error
guard error == nil && data != nil else {
print("network error: \(error)")
return
}
// detect fundamental server errors
if let httpResponse = response as? NSHTTPURLResponse where httpResponse.statusCode != 200 {
// some server error
print("status code was \(httpResponse.statusCode); not 200")
return
}
// detect parsing errors
guard let responseObject = try? NSJSONSerialization.JSONObjectWithData(data!, options: []) as? [String : AnyObject] else {
// some problem parsing the JSON response
print(String(data: data!, encoding: NSUTF8StringEncoding))
return
}
// now parse app-level response to make sure `status` was `true`
guard let success = responseObject!["success"] as? NSNumber else {
print("Problem extracting the `success` value") // we should never get here
return
}
if !success.boolValue {
print("server reported error")
if let errorCode = responseObject!["error_code"] as? NSNumber {
switch (errorCode.integerValue) {
case 1:
print("No password provided")
case 2:
print("Connection failed; probably bad password")
case 3:
print("No databases found")
case 4:
print("Some SQL error")
default:
print("Unknown error code: \(errorCode)") // should never get here
}
}
if let errorMessage = responseObject!["error_message"] as? String {
print(" message=\(errorMessage)")
}
return
}
if let databases = responseObject!["results"] as? [[String : AnyObject]] {
print("databases = \(databases)")
}
}
task.resume()
The percent-escaping code is in a String category:
extension String {
// see RFC 3986
func stringByAddingPercentEncodingForURLQueryValue() -> String? {
let characterSet = NSCharacterSet(charactersInString:"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~")
return self.stringByAddingPercentEncodingWithAllowedCharacters(characterSet)
}
}
A couple of other ancillary observations:
Never send a passwords in the clear. Put them in the body of a POST request (not the URL), and then use a https URL.
I'd personally not use the MySQL password part of the app-level authentication. I'd keep MySQL authentication logic encoded on the server-side, and then use your own user authentication table.
I'm working with Oracle database. I use PHP for my web service and Android as the client which will send blob data to Oracle DB. I use this code
Bitmap imageUpload = BitmapFactory.decodeFile(picturePath);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
imageUpload.compress(Bitmap.CompressFormat.JPEG, 60, baos);
byte[] image_data = baos.toByteArray();
String converted_image = Base64.encodeBytes(image_data);
to decode and send it as base64 encoding, then send it to the web service with this code
ArrayList<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();
nameValuePairs.add(new BasicNameValuePair("TITLE", title));
nameValuePairs.add(new BasicNameValuePair("IMAGES", converted_image));
json = jsonParser.makeHttpRequest(url, "POST", nameValuePairs);
This is my PHP code to insert to oracle db
if(isset($_POST["TITLE"]) && isset($_POST["IMAGES"]))
{
$title = $_POST["TITLE"];
$image = $_POST["IMAGES"];
$gambar = file_get_contents($image);
$query = "INSERT INTO images (TITLE, IMAGES) VALUES (:TITLE, EMPTY_BLOB()) RETURNING IMAGES INTO :IMAGES";
$parse = oci_parse($connect, $query);
$lob_a = oci_new_descriptor($connect, OCI_D_LOB);
oci_bind_by_name($parse, ":TITLE", $title);
oci_bind_by_name($parse, ":IMAGES", $lob_a, -1, OCI_B_BLOB);
oci_execute($parse, OCI_DEFAULT);
if($lob_a->save($gambar))
{
oci_commit($connect);
$lob_a->free();
}
else
{
oci_rollback($connect);
}
}
The PHP code successfully insert the query to the oracle db, but when I see the record the blob does not show anything like this
I think the best solution to this question is to use web service, encode the picture with base64 encoding then decode it while the file transferred.