Loading image and parameters using multipart / form-data in Swift

[ATTENTION]
Since I see that this question is getting noticed more than necessary, I want to tell you not to use any of the following codes.
When I asked a question, Swift was less than a year old, he was moving fast, most libraries were not fast and unstable. I highly recommend you use Alamofire or another library for this kind of task. But do not do it yourself.
[/ WARNING]

I want to upload an image to a Drupal endpoint.

The problem is that I get an HTTP 200 OK response with the content type text / html. The HTML response has a clear message that the node was created correctly. But on the server side, the image is not connected to node.

Also, I do not expect text / html, but application / json, as I specify it in the Accept header.

It already works in an Android application using the 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 that I am not checking the answer on Android ( Void.class ), but everything works fine, and the image attaches to the node on the server side.

Now on iOS in Swift, I tried a few 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 taken from other questions related to stackoverflow:

 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 have tried so many things to make it work that I can no longer see if I am doing something wrong. It seems good to me. The only difference that I see is that I do not save the image in the file system, but directly send the binary data, which in the end is the same.

Here is an image of a request created in Postman (working and receiving json)

Postman

[EDIT] If this can help someone, this is the correct code for 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 
+8
json ios swift multipartform-data
source share
2 answers

I don’t know if this will work for what you are trying to do, but we use it to upload images, the key difference is the use of 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]; 
+2
source share

For any quick JSON 2.0 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}"; } ?> 
+1
source share

All Articles