uploading image in swift with multiple parameters - ios

I am trying to upload an image to a backend client using swift. Trouble is I can't seem to get the formatting correct for the httpbody. I do not want to use a multipart form for uploading as I don't know how to handle that on the backend.
Here is the code I have.. it doesn't work when I view the image online it doesn't display and it is only like 70kb which I know is definitely not how big the image is.
var bodyString: String = "session_id=\(session_id)&location_id=\(location_id)"
bodyString = bodyString.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)!
var body = NSMutableData.alloc()
body.appendData(bodyString.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: true)!)
if image != nil{
var imageData = UIImageJPEGRepresentation(image,0.5)
body = NSMutableData.alloc()
//var imageDataString = imageData.base64EncodedStringWithOptions(NSDataBase64EncodingOptions(rawValue: 0))
bodyString = "session_id=\(session_id)&location_id=\(location_id)&image_data="
bodyString = bodyString.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)!
body.appendData(bodyString.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: true)!)
var imageString = "&image_data=\(imageData)"
body.appendData(imageData)
}
req.HTTPBody = body
UPDATE:
so I decided to go the base64 route but it still doesn't seem to be working I think because I am encoding it as an ntf8string is this the correct way to be doing this?
var imageData = UIImageJPEGRepresentation(image,0.5)
var imageDataString = imageData.base64EncodedStringWithOptions(.allZeros)
body = NSMutableData.alloc()
bodyString = "session_id=\(session_id)&location_id=\(location_id)&tag_type=\(tag_type)&image_data=\(imageDataString)"
bodyString = bodyString.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)!
body.appendData(bodyString.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: true)!)
and on the backend I am decoding it like:
image_data_decoded = base64.b64decode(image_data)

You cannot post binary data in a application/x-www-form-urlencoded request like this. Actually, the code in your question looks like it will try to send a hexadecimal string representation of the binary data, which, probably is not what you intended and even if you did intend to do that, (a) you would have to decode it somehow on the server side; (b) note that this is very inefficient (more than doubles the size of the image payload): and (c) would need to be percent escaped in the request. But I don't think you intended that at all, anyway, so that is probably all moot.
One would generally either create multipart/form-data request as outlined here (in which the uploaded file comes in as a file, e.g. $_FILES in PHP), or one would convert this binary data to text (e.g. using base64) and the the server code has convert the base64 value for image_data key back to binary data.
By the way, I might suggest Alamofire or AFNetworking as alternatives to trying to create requests properly. It doesn't change the underlying issue (you have to pick between a base64 encoding or multipart requests), but it simplifies the Swift code.

Related

Decode a JSON object escaped in a String

I get a response from an API (unfortunately, I cannot change it) that looks something like (just an example):
As bytes => "{\"key\":\"value\"}"
The starting and ending quotes and the escaped quotes are all part of the response, I am solving it in a really ugly way that looks like this:
// (...) Receiving response
guard var responseString = String(bytes: data, encoding: .utf8) else {
print("Response wasn't just a string, great!") // unfortunately, this never happens
return
}
responseString = responseString.trimmingCharacters(in: .whitespacesAndNewlines) // make sure it is trimmed
responseString = String(responseString.dropFirst()) // drop the quote at the start
responseString = String(responseString.dropLast()) // drop the quote at the end
responseString = responseString.replacingOccurrences(of: "\\\"", with: "\"")// convert all \" to " (and hope nothing else is escaped <<< this is really bad!!!)
let responseDataToDecode = responseString.data(using: .utf8)!
// (...) decoding with JSONDecoder
Is there a way to automatically unescape the string and use the JSON object that is contained in it?
If it's double-encoded, then you just need to double-decode. If I understand correctly, the incoming data is like this:
let str = #""{\"key\":\"value\"}""#
// "{\\"key\\":\\"value\\"}"
The first byte is ", the second byte is {, the third byte is \, the fourth byte is ".
That's a JSON-encoded string. So decode that as a string (there was a time when this didn't work because it's a "fragment," but it works fine currently, at least in all my tests):
let decoder = JSONDecoder()
let string = try! decoder.decode(String.self, from: Data(str.utf8)) // {"key":"value"}
And then decode that as your type ([String:String] just for example):
let result = try! decoder.decode([String:String].self, from: Data(string.utf8))
// ["key": "value"]
(IMO this kind of double-encoding is fine, BTW, and I'm not sure why there are so many comments against it. Serializing an arbitrary object makes a lot more sense in many cases than forcing the schema to deal with an arbitrary structure. As long as it's cleanly encoded, I don't see any problem here.)
There's a first step: You need an official documented statement what exactly the format of your data is. It looks like someone took some data, turned it into JSON data, interpreted the data as a string, and then converted the string to a JSON fragment. It's not difficult to decode the JSON fragment, getting a string, turning the string into data, and decoding that data as JSON (starting with JSONSerialization and .allowFragments, probably the only time you should use .allowFragments in your life). Doing it without swearing is hard.
But first you want in writing that this is actually the format. Because I would bet that whoever is responsible for that data format will eventually change it without telling you and break your code.

Only one emoji image display in my app

I am building chat app and need to send and receive emoji images. I use following code to post data :
let myUrl = NSURL(string: "http://test.php")
let request = NSMutableURLRequest(URL:myUrl!)
request.HTTPMethod = "POST"
let postString = " shareImageMessage=\(message)"
request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding)
I can see my message with emojies in debug area as follows :
But only following image display in mysql database :
And following code from php script :
$shareImageMessage = $_POST["shareImageMessage"];
I couldn't understand why only one kind of image transferring.
To send more than one emoticon in the same url simply convert the string you are sending to the server to a base64 and then decode it on the other end of the database. To Encode and Decode strings to base64 you can check this Convert between UIImage and Base64 string. :)
SWIFT: encode the message with base64 like so:
func base64encode(message: NSString)->String{
let plainData = message.dataUsingEncoding(NSUTF8StringEncoding)
return plainData.base64EncodedStringWithOptions(NSDataBase64EncodingOptions.fromRaw(0))!
}
usage : let msgToPost = base64encode("born to be encoded 👻")
then in PHP just use the famous $decodedmsg = base64_decode($str);
this code worked perfectly for me
one more thing..
make sure that your database column supports emoji, you can refer to the question for more detail about fixing the database emoji thing
How to insert utf-8 mb4 character(emoji in ios5) in mysql?

Body of NSMutableRequest consisting of base64encodeString gets truncated while sending as a string

Recently, I am doing a NSUrlSession task to upload multiple images to the backend . I appended all the images in .png representation in an array. I converted the whole array into a base64 String format and tried to send the whole body as a string.
Conversion of imageArray to String -
let imageArrayData: NSData = NSKeyedArchiver.archivedDataWithRootObject(imageArray)
let imageArrayBase64String = imageArrayData.base64EncodedStringWithOptions(NSDataBase64EncodingOptions(rawValue: 0))
Here printing the Array ,I get -
(String) imageArrayBase64String = "YnBsaXN0MDDUAQIDBAUIIiNUJHRvcFgkb2JqZWN0c1gkdmVyc2lvblkkYXJjaGl2ZXLRBgdUcm9vdIABpQkKEBQcVSRudWxs0gsMDQ5WJGNsYXNzWk5TLm9iamVjdHOABKEPgALSEQsSE1dOUy5kYXRhTxIBe8QxiVBORw0KGgoAAAANSUhEUgAAEMAAAAsgCAIAAABnC3DjAAABGWlDQ1BJQ0MgUHJvZmlsZQAAKJFjYGBSSCwoyGESYGDIzSspCnJ3UoiIjFJgf8DAzCDDwMUgwGCUmFxc4BgQ4MMABDAaFXy7xsAIoi/rgszClMcLuFJSi5OB9B8gzk4uKCphYGDMALKVy0sKQOweIFskKRvMXgBiFwEdCGRvAbHTIewTYDUQ9h2wmpAgZyD7A5DNlwRmM4Hs4kuHsAVAbKi9ICDomJKflKoA8r2GoaWlhSaJfiAISlIrSkC0c35BZVFmekaJgiMwpFIVPPOS9XQUjAwMzRgYQOEOUf05EByejGJnEGIIgBCbI8HA4L+UgYHlD0LMpJeBYYEOAwP/VISYmiEDg4A+A8O+OcmlRWVQYxiZjBkYCPEBNDZKYi1QenUAAAAcaURPVAAAAAIAAAAAAAAFkAAAACgAAAWQAAAFkADAfM/Go8QlAABAAElEQVR4AXS8BXQcV7b3K0Pm3jt3IIlBlmzLFjMzMzMztVhqUasZ1ChmZibLsixmmSmGkB1yzIyhgZubTGJ//+rK6GW97721/muvffbZdaqqq7vqVNX+tUJijjZEKTBIztNNyNZKoeol5WlB8dnqibmaKfk68BOy1JNztVKpOkk5mgiSItPkPjLVU/K14rIOpxboJlO1YzNVE3I0IDJzayiMhmBclhoisEhOK9JHPhZPytNILdCiFGqn07STqKrJVI2UfM24rEPx2YcRzKDppRZoYxWkknM1IGxPSp52cp56epFOar5mClUjmYoN1kor0kVyWpFOejHG18E4KQXqqYUaacUaGSVa2XSdPJY+pUAtk6aVxdB"
Creating the body -
let body = "task=doNotification&select_category=\(selectCategory!)&select_type=\(selectType!)&class=\(classid!)&repliable=\(repliable)&select_students=\(selectedStudents)&select_group=\(selectGroup!)&title=\(SbjctOrTtlTxtFld.text!)&text=\(textVieww.text!)&image=\(imageArrayBase64String)&date=\(dateText!)&time=\(timeText!)"
Here printing the body,I get -
(String) body = "task=doNotification&select_category=exams&select_type=check&class=2&repliable=1&select_students=(\n 26,\n 25\n)&select_group=11&title=self&text=Adam <IMG_0002>&image=YnBsaXN0MDDUAQIDBAUIIiNUJHRvcFgkb2JqZWN0c1gkdmVyc2lvblkkYXJjaGl2ZXLRBgdUcm9vdIABpQkKEBQcVSRudWxs0gsMDQ5WJGNsYXNzWk5TLm9iamVjdHOABKEPgALSEQsSE1dOUy5kYXRhTxIBe8QxiVBORw0KGgoAAAANSUhEUgAAEMAAAAsgCAIAAABnC3DjAAABGWlDQ1BJQ0MgUHJvZmlsZQAAKJFjYGBSSCwoyGESYGDIzSspCnJ3UoiIjFJgf8DAzCDDwMUgwGCUmFxc4BgQ4MMABDAaFXy7xsAIoi/rgszClMcLuFJSi5OB9B8gzk4uKCphYGDMALKVy0sKQOweIFskKRvMXgBiFwEdCGRvAbHTIewTYDUQ9h2wmpAgZyD7A5DNlwRmM4Hs4kuHsAVAbKi9ICDomJKflKoA8r2GoaWlhSaJfiAISlIrSkC0c35BZVFmekaJgiMwpFIVPPOS9XQUjAwMzRgYQOEOUf05EByejGJnEGIIgBCbI8HA4L+UgYHlD0LMpJeBYYEOAwP/VISYmiEDg4A+A8O+OcmlRWVQYxiZjBkYCPEBNDZKYi1QenUAAAAcaURPVAAAAAIAAAAAAAAFkAAAACgAAAWQAAAFkADAfM/Go8QlAABAAElEQVR4AXS8BXQcV7b3K0Pm3jt3IIlBlmzLFjMzMzMztVhqUasZ1ChmZibLsixmmSmGkB1yzIyhgZubTGJ//+rK6GW97721/muvffbZdaqqq7vqVNX+tUJijjZEKTBIztNNyNZKoeol5WlB8dnqibmaKfk68BOy1JNztVKpOkk5mgiSItPkPjLVU/K14rIOpxboJlO1YzN"
As you can notice that the body don't even pass the full encoded string of the array and it also is not passing the last 2 parameters i.e date & time.
So why is it so ?
Calling the web service -
func sendAPIRequest(urlpath:NSString,body: NSString , completion: (result: NSMutableDictionary, error: AnyObject?)-> Void ) -> Void
{
let url:NSURL = NSURL(string: urlpath as String)!
let request = NSMutableURLRequest(URL: url)
request.HTTPMethod = "POST"
let bodydata = body.dataUsingEncoding(NSUTF8StringEncoding)
request.HTTPBody = bodydata
let config = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: config)
let task = session.dataTaskWithRequest(request) { (data, response, error) -> Void in
do
{
let resultdic = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers) as? NSMutableDictionary
//print(resultdic!)
completion(result: resultdic!,error: nil)
}
catch
{
print("error")
}
}
task.resume()
A couple of observations:
It looks like you're looking at body in the debugger. That will truncate the string (though usually it shows ellipses at the end to indicate that it was truncated). I'd suggest you try print(body) and see if you see the whole string there.
Your selectStudents is an NSArray and when you do string interpolation on that, it includes a newline character in it. The net effect is that this x-www-form-urlencoded request is not well-formed and will likely be rejected by the server. There are a couple of ways of resolving with this
If your server is really expecting a string of the form select_students=(\n 26,\n 25\n) (which I'd be very surprised if that's really what you want), you'd percent escape that string:
let selectedStudentsString = "selectedStudents=\(selectedStudents)".stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLQueryParameterAllowedCharacterSet())!
and that will yield:
selectedStudents=(%0A%20%20%20%20foo,%0A%20%20%20%20bar%0A)
where URLQueryParameterAllowedCharacterSet is defined as:
extension NSCharacterSet {
/// Returns the character set for characters allowed in the individual parameters within a query URL component.
///
/// The query component of a URL is the component immediately following a question mark (?).
/// For example, in the URL `http://www.example.com/index.php?key1=value1#jumpLink`, the query
/// component is `key1=value1`. The individual parameters of that query would be the key `key1`
/// and its associated value `value1`.
///
/// According to RFC 3986, the set of unreserved characters includes
///
/// `ALPHA / DIGIT / "-" / "." / "_" / "~"`
///
/// In section 3.4 of the RFC, it further recommends adding `/` and `?` to the list of unescaped characters
/// for the sake of compatibility with some erroneous implementations, so this routine also allows those
/// to pass unescaped.
class func URLQueryParameterAllowedCharacterSet() -> Self {
return self.init(charactersInString: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~/?")
}
}
See https://stackoverflow.com/a/35912606/1271826 for alternative percent-escaping options. But avoid using stringByAddingPercentEncodingWithAllowedCharacters with any of the existing character sets (e.g. URLQueryAllowedCharacterSet), because they all allow reserved characters to pass unescaped.
If you really want this received as an array in an x-www-form-urlencoded request by your server code, you'd probably want:
var selectedStudentsArray = [String]()
for index in 0 ..< selectedStudents.count {
selectedStudentsArray.append("selectedStudents[]=\(selectedStudents[index])")
}
let selectedStudentsString = selectedStudentsArray.joinWithSeparator("&")
and that will yield:
selectedStudents[]=26&selectedStudents[]=25
That's the right way to send an array of values in an x-www-form-urlencoded request.
Your text value includes reserved characters, too. You should be percent escaping this (and anything else that might include characters outside of a-z, A-Z, 0-9, and -, ., _, ~, /, and ?). Again, when doing x-www-form-urlencoded request like this, all reserved characters must be percent escaped.
You are taking your array of images and building a NSKeyedArchiver. This is going to be one big, opaque, blob object as far as your web service is concerned. If your intent is to treat this as a single blob on the server, that's fine, but if you were hoping to then extract the images out of that, you're making your life unnecessarily complicated.
You can either:
Base-64 encode the images separately and then use the same format I suggested for selectStudents, e.g.
image[]=Bsa...JJH&image[]=5YJ...mVj
Note, you should use the original contents of the data (e.g. get the NSData directly from the original file/asset, avoiding UIImage altogether) or, if you really need to round-trip it through a UIImage (which I'd discourage if you can prevent it), then use UIImagePNGRepresentation or UIImageJPEGRepresentation to create a new NSData from the UIImage.
You might consider using a multipart/form-data request and then the individual images (again, with the aforementioned comment about getting the NSData notwithstanding) can be stored individually. If you do proper multipart/form-data request, it avoids needing to do any percent-escaping anywhere, too, avoids base-64 encoding (which makes the transmission 33% larger than it needs to be), etc.
Bottom line, properly creating these sorts of requests is very, very complicated, and unless you really want to unravel all of these idiosyncrasies, I'd really encourage you to consider Alamofire, which gets you out of these weeds. And if you truly feel compelled to reinvent the wheel, I'd suggest you research these topics individually, as this is far too broad for a single question. Each of my points above probably warrants a separate question.

Swift: How to save data of different types in a file

I want to save data including string, number, date and coordinate in a file, and then transmit the file to a server. How to do this using swift?
And I'd like to process these data from the server in the future. What type of file is better to save them?
If i am getting this right you can use NSData
First you have to create a dictionary like this
var dictionary = [String:AnyObject]()
dictionary["age"] = 13
dictionary["name"] = "Mike"
Then you have to transform this dictionary into NSData using nsjsonserialization
if let data = NSJSONSerialization.dataWithJSONObject(dictionary, options:NSJSONWritingOptions(0), error:nil) as NSData? {
request.HTTPBody = data
}
But this is always depend on what the server is able to understand
Hope i helped. Sorry for my english

POST base64 encoded image string to PHP

I've read through numerous posts and watched some videos.
Problem: I'm unable to POST a base64 string image to PHP server and save to MySQL database without corruption.
What I've attempted: I've attempted passing the base64 encoded string of the image just like I would pass a string or int as a POST parameter. I verified that the POST data is successfully making it to the PHP server and it is getting saved in the database. However, when I extract the base64 encoded string and decode it so the image can be rendered in the browser, it does not work. It only shows a broken image link instead of the image. It seems as though something is happening to corrupt the data in the POST process. I say this because I copied the base64 string directly from a println() statement in Xcode and pasted it into the PHP file where it was decoded and displayed perfectly. I'm able to upload base64 strings of images from my Android app perfectly fine. The problems seems to be with how I'm doing it in Swift.
After some research, I see where I may need to specify a boundary around the image parameter. I'm not positive about this though because I'm not sure if you just need a boundary specified when you send the image file directly or if you also need it when sending a base64 encoded string of the image.
Can anyone tell me if I need to set a boundary around the base64 encoded string parameter before sending it to the PHP server? Any idea what I could be doing wrong?
let image: UIImage = imgProfilePic.image!
let size = CGSizeApplyAffineTransform(image.size, CGAffineTransformMakeScale(0.1, 0.1))
let hasAlpha = false
let scale: CGFloat = 0.0 // Automatically use scale factor of main screen
UIGraphicsBeginImageContextWithOptions(size, !hasAlpha, scale)
image.drawInRect(CGRect(origin: CGPointZero, size: size))
let scaledImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
var imageData = UIImageJPEGRepresentation(scaledImage, 0.9)
let base64String = imageData.base64EncodedStringWithOptions(.allZeros)
var cd = CoreDataUser(pstrContext: "this")
var params = "strUsername=" + cd.getUsername()
params = params + "&strPassword=" + cd.getPassword()
params = params + "&blbProfilePic=" + base64String
Earlier I referred you to https://stackoverflow.com/a/14803292/1271826 which shows one fairly limited and tactical solution to the presence of the + characters in the base-64 string. This consisted of replacing the + characters with %2B.
But I notice that you're also including user name and password in your parameters. This would suggest that you really want a more generalized solution, percent escaping all of the reserved characters. If you don't do that, this wouldn't work if the password included reserved characters (such as + or &).
When submitting parameters like this, you should percent escape the values. For example, you might define a character set to be only the "unreserved" characters (as defined by RFC 3986), namely the alphanumeric characters plus -, ., _, and ~:
extension NSCharacterSet {
class func URLQueryValueAllowedCharacterSet() -> NSCharacterSet {
return NSCharacterSet(charactersInString: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~")
}
}
Then you can do something like:
func percentEscapeString(string: String) -> String {
return string.stringByAddingPercentEncodingWithAllowedCharacters(.URLQueryValueAllowedCharacterSet())!
}
Or, if you like the old CFURLCreateStringByAddingPercentEscapes function, you can do:
func percentEscapeString(string: String) -> String {
return CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault,
string,
nil,
":/?#!$&'()*+,;=",
CFStringBuiltInEncodings.UTF8.rawValue) as! String;
}
The previous examples are more intuitive, but this latter approach is a little more efficient.
But regardless of how you implement it, you can now use this function to percent escape your strings:
let params = "strUsername=" + percentEscapeString(cd.getUsername()) +
"&strPassword=" + percentEscapeString(cd.getPassword()) +
"&blbProfilePic=" + percentEscapeString(base64String)

Resources