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.