Uploading an image and parameters with multipart/form-data in Swift - ios

[WARNING]
As I am seeing that this question is getting noticed more than it should,
I want to tell you not to use any of the following code.
At the time I asked the question, Swift had less than a year, was moving fast, most of the libraries were not Swift-friendly and unstable.
I strongly recommend you to try using Alamofire or another library for that kind of task. But don't do it yourself.
[/WARNING]
I want to upload an image to a Drupal endpoint.
The problem I have is that I receive an HTTP 200 OK response with text/html content type. In the HTML response, there is a clear message that the node has been correctly created. But on the server side the image is not associated with the node.
Also I am not expecting text/html but application/json as I specify it in the Accept header.
It already works in the Android app using Android Rest Template. Here is the code for reference:
String url = getUrl("node/{info_id}/attach_file");
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
if (user.isLoggedIn()) {
headers.add(user.getSessionName(), user.getSessionId());
headers.add("X-CSRF-Token", user.getToken());
headers.add("Cookie", user.getSessionName() + "=" + user.getSessionId());
}
MultiValueMap<String, Object> parts = new LinkedMultiValueMap<>();
parts.add("files[field_mobileinfo_image]",
new FileSystemResource(info.getImageUri()));
parts.add("field_name", "field_mobileinfo_image");
HttpEntity<MultiValueMap<String, Object>> request = new HttpEntity<>(parts, headers);
return getRestTemplate().exchange(url, HttpMethod.POST, request, Void.class, info.getId()).getBody();
I know I don't check the response in Android (Void.class) but everything works fine and the image is attached to the node on the server side.
Now on iOS in Swift I tried multiple things.
With AFNetworking:
func upload(mobileInfo: MobileInfo) {
let user = userService.load()
let url = Config.buildUrl("")
let manager = AFHTTPRequestOperationManager(baseURL: NSURL(string:url)!)
let serializer = AFHTTPRequestSerializer()
serializer.setValue(user.sessionId, forHTTPHeaderField: user.sessionName)
serializer.setValue(user.token, forHTTPHeaderField: "X-CSRF-Token")
serializer.setValue("\(user.sessionName)=\(user.sessionId)", forHTTPHeaderField: "Cookie")
manager.requestSerializer = serializer
manager.responseSerializer.acceptableContentTypes.removeAll(keepCapacity: false)
manager.responseSerializer.acceptableContentTypes.insert("application/json")
let imageData = UIImageJPEGRepresentation(mobileInfo.image, 0.3)
manager.POST("/node/\(mobileInfo.id)/attach_file", parameters: nil, constructingBodyWithBlock: { (formData) -> Void in
formData.appendPartWithFileData(
imageData,
name: "files[field_mobileinfo_image]",
fileName: "field_mobileinfo_image",
mimeType: "image/jpeg")
formData.appendPartWithFormData("field_mobileinfo_image".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: true), name: "field_name")
},
success: { (operation, data) -> Void in
println(data)
}) { (operation, error) -> Void in
println(error)
}
}
Manually with information grabbed from other stackoverflow questions:
func upload2(mobileInfo: MobileInfo) {
let user = userService.load()
let imageData = UIImageJPEGRepresentation(mobileInfo.image, 0.3)
let url = NSURL(string:Config.buildUrl("/node/\(mobileInfo.id)/attach_file"))!
println(url)
var request = NSMutableURLRequest(URL: url)
var session = NSURLSession.sharedSession()
request.HTTPMethod = "POST"
var boundary = "---------------------------14737809831466499882746641449"
var contentType = "multipart/form-data; boundary=\(boundary)"
println(contentType)
request.addValue(contentType, forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
request.addValue("\(user.sessionName)=\(user.sessionId)", forHTTPHeaderField: "Cookie")
request.addValue(user.sessionId, forHTTPHeaderField: user.sessionName)
request.addValue(user.token, forHTTPHeaderField: "X-CSRF-Token")
println(request.allHTTPHeaderFields)
var body = NSMutableData()
body.appendData("\r\n--\(boundary)\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
body.appendData("Content-Disposition: form-data; name=\"field_name\"\r\n\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
body.appendData("field_mobileinfo_image".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: true)!)
body.appendData("\r\n--\(boundary)\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
body.appendData("Content-Disposition: form-data; name=\"files[field_mobileinfo_image]\"; filename=\"img.jpg\"\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
body.appendData("Content-Type: application/octet-stream\r\n\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
body.appendData(imageData)
body.appendData("\r\n--\(boundary)\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
var returnData = NSURLConnection.sendSynchronousRequest(request, returningResponse: nil, error: nil)
var returnString = NSString(data: returnData!, encoding: NSUTF8StringEncoding)
println("returnString \(returnString)")
}
With SRWebClient:
func upload3(mobileInfo: MobileInfo) {
let user = userService.load()
let imageData:NSData = NSData(data: UIImageJPEGRepresentation(mobileInfo.image, 0.3))
SRWebClient.POST("http://master.test.lesfrontaliers.lu/node/\(mobileInfo.id)/attach_file")
.headers(["Accept": "application/json",
user.sessionName: user.sessionId,
"X-CSRF-Token": user.token,
"Cookie": "\(user.sessionName)=\(user.sessionId)"])
.data(imageData, fieldName:"files[field_mobileinfo_image]", data:["field_name":"field_mobileinfo_image"])
.send({ (response: AnyObject!, status: Int) -> Void in
println(status)
println(response)
},failure:{(error:NSError!) -> Void in
println(error)
})
}
Please save me! ;-)
I tried so many things to make it work that I can't see anymore if I am doing something wrong. It seems ok for me. The only difference I can see is that I am not storing the image on the filesystem but directly sending the binary data which is the same thing in the end.
Here is an image of the request created in Postman (working and receiving json)
[EDIT] If it can help someone here is the correct code of the wrong part of the above manual request:
var body = NSMutableData()
body.appendData("--\(boundary)\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
body.appendData("Content-Disposition: form-data; name=\"field_name\"\r\n\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
body.appendData("field_mobileinfo_image\r\n".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: true)!)
body.appendData("--\(boundary)\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
body.appendData("Content-Disposition: form-data; name=\"files[field_mobileinfo_image]\"; filename=\"img.jpg\"\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
body.appendData("Content-Type: image/jpeg\r\n\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
body.appendData(imageData)
body.appendData("\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
body.appendData("--\(boundary)--\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
request.HTTPBody = body

Don't know if this will work for what you are trying to do but we use this to upload images, the key difference is to use filename and Content-type:
[self appendBody:body data:[NSString stringWithFormat:#"Content-Disposition: form-data; name=\"%#\"; filename=\".jpg\"\r\n", key]];
[self appendBody:body data:#"Content-Type: image/jpeg\r\n\r\n"];
[body appendData:imageData];

For any swift 2.0 JSON Request AND PHP code :- ( Manual )
let imageData = UIImageJPEGRepresentation(userImage, 0.3)
let url:NSURL = NSURL(string: serverURL!)! // Give ur request URL
let request = NSMutableURLRequest(URL: url)
request.HTTPMethod = "POST"
let boundary = "---------------------------14737809831466499882746641449"
let contentType = "multipart/form-data; boundary=\(boundary)"
request.addValue(contentType, forHTTPHeaderField: "Content-Type")
let body = NSMutableData()
body.appendData("--\(boundary)\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
body.appendData("Content-Disposition: form-data; name=\"userfile\"; filename=\"img.jpg\"\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
body.appendData("Content-Type: image/jpeg\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
body.appendData("Content-Transfer-Encoding: binary\r\n\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
body.appendData(imageData!)
body.appendData("\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
body.appendData("--\(boundary)--\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
request.HTTPBody = body
PHP Code :-
<?php
//http://192.168.1.154/Contact/uploadImgwebservice.php
//print the username and password using php
echo $_POST[‘username’];
echo $_POST[‘password’];
//upload your file
$uploaddir = ‘./uploads/’;
$file = basename($_FILES[‘userfile’][‘name’]);
$uploadfile = $uploaddir . $file;
if (move_uploaded_file($_FILES[‘userfile’][‘tmp_name’], $uploadfile)) {
echo “http://192.168.1.154/Contact/uploads/{$file}”;
}
?>

Related

Swift - Sending binary file to a server (convert to string request)

Here is my function for building a request to send to a server. This code works perfectly fine when it's a small, plain-text file. However, when I pass it a binary file, it crashes on the line indicated below (when it tries to convert that to a utf string and blows up b/c the conversion does not work, as it creates a nil value):
func buildFilePushRequest(fromUrl url: URL, httpBody: Data? = nil, fileName: String) -> NSMutableURLRequest? {
let cachePolicy = NSURLRequest.CachePolicy.reloadIgnoringLocalCacheData
let request = NSMutableURLRequest(url: url, cachePolicy: cachePolicy, timeoutInterval: 2.0)
request.httpMethod = "POST"
// Set Content-Type in HTTP header.
let boundaryConstant = "Boundary-7MA4YWxkTLLu0UIW"; // This should be auto-generated.
let contentType = "multipart/form-data; boundary=" + boundaryConstant
let fileName = fileName
request.setValue(contentType, forHTTPHeaderField: "Content-Type")
request.setValue("keep-alive", forHTTPHeaderField: "Connection")
var dataString = "--\(boundaryConstant)\r\n"
dataString += "Content-Disposition: form-data; name=file; filename=\"\(fileName)\"\r\n"
dataString += "Content-Type: octet-stream\r\n\r\n"
dataString += String(data: httpBody!, encoding: .utf8)! <-- CRASHES HERE
dataString += "\r\n"
dataString += "--\(boundaryConstant)--\r\n"
print("dataString: \(dataString)")
let requestBodyData = dataString.data(using: .utf8)
request.httpBody = requestBodyData
return request
}
I've read a bit that base64 is a better way to go rather than utf8 for binary type data, but I'm not really sure how best to modify the code above to use that instead, or if there is a better solution? Thanks in advance!
If the httpBody parameter can contain binary data you could compound the entire http body as Data
First of all please use native URLRequest, it's mutable as variable and why is the return value optional?
func buildFilePushRequest(fromUrl url: URL, httpBody: Data? = nil, fileName: String) -> URLRequest {
var request = URLRequest(url: url, cachePolicy: .reloadIgnoringLocalCacheData, timeoutInterval: 2.0)
...
var data = Data("--\(boundaryConstant)\r\n".utf8)
data += Data("Content-Disposition: form-data; name=file; filename=\"\(fileName)\"\r\n".utf8)
data += Data("Content-Type: octet-stream\r\n\r\n".utf8)
if let body = httpBody { data += body }
data += Data("\r\n".utf8)
data += Data("--\(boundaryConstant)--\r\n".utf8)
request.httpBody = data

How to upload the image to amazon bucket in swift3?

I want to post the customer signature image to amazon s3 bucket_type server with an api...I am referring with different tutorials...I didn't get enough information from any of tutorials...I am trying with following code but I am getting nil result...
funcUpload(service:String,imagedata:Data,completion:#escaping (_ result:NSDictionary?,_ error:AnyObject?) -> ()){
let boundaryConstant = "----------V2y2HFg03eptjbaKO0j1"
let urlpath:String = "http:myurl" + service
let url:NSURL = NSURL(string:urlpath)!
let request = NSMutableURLRequest(url: url as URL)
request.httpMethod = "P0ST"
request.cachePolicy = NSURLRequest.CachePolicy.reloadIgnoringLocalCacheData
request.httpShouldHandleCookies = false
let contentType = "multipart/form-data; boundary=\(boundaryConstant)"
request.setValue(contentType, forHTTPHeaderField: "Content-Type"
let body = NSMutableData()
if imagedata != nil{
body.append("--\(boundaryConstant)\r\n".data(using: String.Encoding.utf8)!)
body.append("Content-Disposition: form-data; name=\"\("universalapparealproduct")\" ; filename=\"image.jpg\"\r\n".data(using: String.Encoding.utf8)!)
body.append("Content-Type: \("universalapparealproduct")\r\n\r\n".data(using: String.Encoding.utf8)!)
body.append(imagedata)
body.append("\r\n".data(using: String.Encoding.utf8)!)
}
body.append("--\(boundaryConstant)--\r\n".data(using: String.Encoding.utf8)!)
request.httpBody = body as Data
let config = URLSessionConfiguration.ephemeral
self.session = URLSession(configuration: config)
let task = session.dataTask(with: request as URLRequest) { (data, response, error) -> Void in
if let receivedData = data
{
do
{let resultDic = try JSONSerialization.jsonObject(with: receivedData, options: JSONSerialization.ReadingOptions.mutableContainers) as? NSDictionary
completion(resultDic!,nil)
}
catch let error
{
NSLog("\(error)")
}
}
else if let errorMessage = error
{
completion(nil,errorMessage as AnyObject?)
}
self.session.invalidateAndCancel()
}
task.resume()
}
}
Please can any one help me to resolve this issue.
Thanks in advance.
let urlpath:String = "http:myurl" + service looks like a typo.
I assume that the service variable contain a url with pre-signed credentials that you can upload to. Make sure that is correct. If not you have to have a server serve you pre-signed credentials.
use PUT not POST
I would expect that the body of the request would just be the imageData, but you seem to be adding a lot of stuff to body. I don't know what kind of file you want to upload but you are making a weird file. If that is your intent is fine, but just we aware that is what you are doing. If indenting to upload a custom file type, I would move all of that code to a different function and then have another function that just uploads the data.
the only HTTPHeader field that you have to set is Content-Length. It must be set to the size of the body that you send.
The expected result of the request is data with a length of 0 and no error. So your JSONSerialization in the response block in wrong.
func imageUpload(service:String,token:String,imagedata:Data,completion:#escaping (_ result:NSDictionary?,_ error:AnyObject?) -> ())
{
let boundaryConstant = "----------V2y2HFg03eptjbaKO0j1"
let params = NSMutableDictionary()
let bucket_name = "userspecifiedbucketname"
params.setObject("(bucket_name)", forKey:"bucket_type" as NSCopying)
let urlpath:String = "http://\(myurladdress)/" + service
let url:NSURL = NSURL(string:urlpath)!
print("URL:\(url)")
var request = URLRequest(url: url as URL)
request.httpMethod = "POST"
request.cachePolicy = NSURLRequest.CachePolicy.reloadIgnoringLocalCacheData
let contentType = "multipart/form-data; boundary=\(boundaryConstant)"
request.setValue(contentType, forHTTPHeaderField: "Content-Type")
if token != ""
{
request.setValue("\(token)", forHTTPHeaderField: "Authorization")
}
let body = NSMutableData()
for param in params {
body.append("--\(boundaryConstant)\r\n" .data(using: String.Encoding.utf8,allowLossyConversion: false)! )
body.append("Content-Disposition: form-data; name=\"\(param.key)\"\r\n\r\n" .data(using: String.Encoding.utf8,allowLossyConversion: false)!)
body.append("\(param.value)\r\n" .data(using: String.Encoding.utf8,allowLossyConversion: false)!)
}
body.append("--\(boundaryConstant)\r\n".data(using: String.Encoding.utf8,allowLossyConversion: false)!)
body.append("Content-Disposition: form-data; name=\"\("file")\" ; filename=\"image.jpg\"\r\n".data(using: String.Encoding.utf8,allowLossyConversion: false)!)
body.append("Content-Type: image/jpeg\r\n\r\n".data(using: String.Encoding.utf8,allowLossyConversion: false)!)
body.append(imagedata)
body.append("\r\n".data(using: String.Encoding.utf8,allowLossyConversion: false)!)
// image end
body.append("--\(boundaryConstant)--\r\n".data(using: String.Encoding.utf8,allowLossyConversion: false)!)
request.httpBody = body as Data
let postLength = "\(body.length)"
request.setValue(postLength, forHTTPHeaderField: "Content-Length")
}
NOTE: (Here bucket_name should be same as server side bucket_name )

How to send a request with alamofire with xml Body

I installed Alamofire in my project and now here is what I have done.
I installed postman and I put my url and inside body a xml object and I got my result.
Here is a picture of what I exactly have done with postman
How can I now use Alamofire or SWXMLHash to send it as I send it with postman
Thanks in advance!
EDIT
I tried this from another question:
Alamofire.request(.POST, "https://something.com" , parameters: Dictionary(), encoding: .Custom({
(convertible, params) in
let mutableRequest = convertible.URLRequest.copy() as! NSMutableURLRequest
let data = (self.testString as NSString).dataUsingEncoding(NSUTF8StringEncoding)
mutableRequest.HTTPBody = data
return (mutableRequest, nil)
}))
.responseJSON { response in
print(response.response)
print(response.result)
}
}
But it didn't send anything
This is the log:
Optional( { URL:
https://something.com } { status code: 200, headers {
Connection = "keep-alive";
"Content-Length" = 349;
"Content-Type" = "application/xml";
Date = "Wed, 02 Nov 2016 21:13:32 GMT";
Server = nginx;
"Strict-Transport-Security" = "max-age=31536000; includeSubDomains"; } })
FAILURE
EDIT
NEVER FORGET TO PASS parameters if you don't have simple add this , parameters: Dictionary()
Using Swift 3 and Alamofire 4
let stringParams : String = "<msg id=\"123123\" reqTime=\"123123\">" +
"<params class=\"API\">" +
"<param name=\"param1\">123213</param>" +
"<param name=\"param2\">1232131</param>" +
"</params>" +
"</msg>"
let url = URL(string:"<#URL#>")
var xmlRequest = URLRequest(url: url!)
xmlRequest.httpBody = stringParams.data(using: String.Encoding.utf8, allowLossyConversion: true)
xmlRequest.httpMethod = "POST"
xmlRequest.addValue("application/xml", forHTTPHeaderField: "Content-Type")
Alamofire.request(xmlRequest)
.responseData { (response) in
let stringResponse: String = String(data: response.data!, encoding: String.Encoding.utf8) as String!
debugPrint(stringResponse)
}
With Swift 3 and Alamofire 4 you would create a custom ParameterEncoding. As with any other XML encoded body, SOAP messages can use this parameter encoding as in the following example. Other XML body encodings can be created similarly (check the line where it says urlRequest.httpBody = ...):
struct SOAPEncoding: ParameterEncoding {
let service: String
let action: String
func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest {
var urlRequest = try urlRequest.asURLRequest()
guard let parameters = parameters else { return urlRequest }
if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil {
urlRequest.setValue("text/xml", forHTTPHeaderField: "Content-Type")
}
if urlRequest.value(forHTTPHeaderField: "SOAPACTION") == nil {
urlRequest.setValue("\(service)#\(action)", forHTTPHeaderField: "SOAPACTION")
}
let soapArguments = parameters.map({key, value in "<\(key)>\(value)</\(key)>"}).joined(separator: "")
let soapMessage =
"<s:Envelope xmlns:s='http://schemas.xmlsoap.org/soap/envelope/' s:encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'>" +
"<s:Body>" +
"<u:\(action) xmlns:u='\(service)'>" +
soapArguments +
"</u:\(action)>" +
"</s:Body>" +
"</s:Envelope>"
urlRequest.httpBody = soapMessage.data(using: String.Encoding.utf8)
return urlRequest
}
}
And then use it like that:
Alamofire.request(url, method: .post, parameters: ["parameter" : "value"], encoding: SOAPEncoding(service: "service", action: "action"))
Assuming you that you're missing valid HTTP headers in your request, the updated request could look like:
Alamofire.request(.POST, "https://something.com", parameters: Dictionary() , encoding: .Custom({
(convertible, params) in
let mutableRequest = convertible.URLRequest.copy() as! NSMutableURLRequest
let data = (self.testString as NSString).dataUsingEncoding(NSUTF8StringEncoding)
mutableRequest.HTTPBody = data
mutableRequest.setValue("application/x-www-form-urlencoded; charset=utf-8", forHTTPHeaderField: "Content-Type")
return (mutableRequest, nil)
}))
.responseJSON { response in
print(response.response)
print(response.result)
}
}
So, basically you should add one line
mutableRequest.setValue("application/x-www-form-urlencoded; charset=utf-8", forHTTPHeaderField: "Content-Type")
Update:
Try same, but use responseData or responseString instead of responseJSON because it is possible that your response is not JSON

Sending video on server by multipart form data

I have a problem with pushing video on the server. My API has two parameters in body: details and file and I must authorize it by token in header.
At the beginning my file was prepared and exported to the URL that I am pushing to method. Next with details (string value) I am trying to prepare it to send on the server.
After I run my app I have got this error:
Error Domain=NSCocoaErrorDomain Code=3840 "No value." UserInfo={NSDebugDescription=No value.}
Server in respond returns all data about pushed video in JSON
I used this tutorial to try make some first step:
https://mindfiremobile.wordpress.com/2014/01/27/upload-video-to-server-using-multiparts/
It my first steps with pushing video on the server and I will be glad for help.
func postDetailsWithVideo(details: String, file: NSURL) {
let url = serverURL.URLByAppendingPathComponent("api/details")
let videoData = NSData.init(contentsOfURL: file)
let kBoundary = "---------------------------14737809831466499882746641449"
let kStartTag = "--%#\r\n"
let kEndTag = "\r\n"
let kContent = "Content-Disposition: form-data; name=\"%#\"\r\n\r\n"
let body = NSMutableData()
//details data
body.appendData(String(format: kStartTag, kBoundary).dataUsingEncoding(NSUTF8StringEncoding)!)
body.appendData(String(format: kContent, "storyDetails").dataUsingEncoding(NSUTF8StringEncoding)!)
body.appendData(details.dataUsingEncoding(NSUTF8StringEncoding)!)
body.appendData(String(format: kEndTag).dataUsingEncoding(NSUTF8StringEncoding)!)
//Video data
body.appendData(String(format: kStartTag, kBoundary).dataUsingEncoding(NSUTF8StringEncoding)!)
body.appendData("Content-Disposition: form-data; file=\"flv\"\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
body.appendData("Content-Type: application/octet-stream\r\n\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
body.appendData(NSData(data: videoData!))
body.appendData(String(format: kEndTag).dataUsingEncoding(NSUTF8StringEncoding)!)
// close form
body.appendData("--\(kBoundary)--\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
let contentType = "multipart/form-data; boundary=\(kBoundary)"
let request = NSMutableURLRequest(URL: url)
request.HTTPMethod = "POST"
request.setValue("Bearer \(DataManager().getAccessToken())", forHTTPHeaderField: "Authorization")
request.setValue(contentType, forHTTPHeaderField: "Content-Type")
request.HTTPBody = body
print(body)
postDataOnTheServer(request, apiMethod: "api/details")
}
func postDataOnTheServer(request: NSMutableURLRequest, apiMethod: NSString) {
let currentRequest = request
let task = NSURLSession.sharedSession().dataTaskWithRequest(currentRequest){ data, response, error in
if error != nil{
self.delegate?.errorOccured(apiMethod, error: error!)
return
}
do {
let result = try NSJSONSerialization.JSONObjectWithData(data!, options: []) as? [String:AnyObject]
self.delegate?.serverResponseFromAPIMethod(apiMethod, result: result!)
} catch {
print("response - some error")
print(error) //do something with me
}
}
task.resume()
}
Its a server issue.Ask your backend not to echo anything and you will get the response.

Uploading image and text values to server

I'm trying to create a new object with an image and some other values using multipart but I can't get it to work.
This is the code I was using without uploading an image (this works):
let request = NSMutableURLRequest(URL: NSURL(string: self.submitLink)!)
let session = NSURLSession.sharedSession()
let params = [
"article": [
"name" : name,
"description" : fullDescription.text!,
"subdescription" : sDescription,
"category_id" : categoriesArray[self.categoryPicked]["id"] as! Int,
"location_id" : locationsArray[self.locationPicked]["id"] as! Int,
"Xcoordinate" : self.lat,
"Ycoordinate" : self.lng,
"user_id" : User.sharedInstance.userId,
"article_images_attributes[][image]" : ""
]
]
request.HTTPBody = try? NSJSONSerialization.dataWithJSONObject(params, options: NSJSONWritingOptions())
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
request.HTTPMethod = "POST"
let task = session.dataTaskWithRequest(request, completionHandler: { data, response, error -> Void in
print("article created.")
})
task.resume()
and this is the code I'm using at the moment (not working):
let params = [
"article[name]" : name,
"article[description]" : fullDescription.text!,
"article[subdescription]" : sDescription,
"article[category_id]" : categoriesArray[self.categoryPicked]["id"] as! Int,
"article[location_id]" : locationsArray[self.locationPicked]["id"] as! Int,
"article[Xcoordinate]" : self.lat,
"article[Ycoordinate]" : self.lng,
"article[user_id]" : User.sharedInstance.userId
]
let boundaryConstant = "----------V2ymHFg03ehbqgZCaKO6jy"
let fileParamConstant = "article[article_images_attributes][][image]"
let session = NSURLSession.sharedSession()
let request = NSMutableURLRequest(URL: NSURL(string: self.submitLink)!)
request.HTTPMethod = "POST"
let contentType = "multipart/form-data; boundary=\(boundaryConstant)"
request.setValue(contentType, forHTTPHeaderField: "Content-Type")
let body = NSMutableData()
for param in params {
print(param)
body.appendData("--\(boundaryConstant)\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
body.appendData("Content-Disposition: form-data; \(param.key)=\r\n\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
body.appendData("\(param.value)\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
}
let imageData = UIImageJPEGRepresentation(self.photoArray[0], 1)
if imageData != nil {
body.appendData("--\(boundaryConstant)\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
body.appendData("Content-Disposition: form-data; name=\(fileParamConstant); filename=\"image.jpg\"\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
body.appendData("Content-Type: application/octet-stream\r\n\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
body.appendData(imageData!)
body.appendData("\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
}
body.appendData("--\(boundaryConstant)\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
request.HTTPBody = body
let task = session.dataTaskWithRequest(request, completionHandler: { data, response, error -> Void in
print(request)
print(data)
})
task.resume()
Whenever I try to submit my form the server replies with:
NoMethodError (undefined method `name' for nil:NilClass)
So I assume there is something wrong with the way I defined my params? Could someone explain it to me a bit more in-depth so I know what I did wrong?
It looks like you are using the wrong Content-Type for your image and the name may not be correct.
Try something like this to set up your image:
NSMutableData *body = [NSMutableData data];
double compressionRatio=0.7;
NSData* imageData = UIImageJPEGRepresentation((UIImage*)dataParam, compressionRatio);
[self utfAppendBody:body data:[NSString stringWithFormat:#"Content-Disposition: form-data; name=\"%#\"; filename=\".jpg\"\r\n", paramName]];
[self utfAppendBody:body data:#"Content-Type: image/jpeg\r\n\r\n"];
[body appendData:imageData];
With this helper function:
- (void)utfAppendBody:(NSMutableData *)body data:(NSString *)data {
[body appendData:[data dataUsingEncoding:NSUTF8StringEncoding]];
}

Resources