Swift 4 Upload Image to Php Server
This postal service presume yous have some experience working with URLSession and some brief knowledge on how HTTP works. This postal service volition be focusing on multipart information content blazon.
TL;DR - Jump to the lawmaking
You might have come up across scenarios where your app need to send some epitome data to a server API endpoint (eg: user updates contour flick). A quick search on Google / Stack Overflow volition get you many answers recommending yous to use Alamofire / AFNetworking, but is it really necessary to install a library merely for handling image upload? What is happening under the hood of Alamofire / AFNetworking library?
Some answer from Stack Overflow might contain weird looking cord like \r\n--61752208 , what does this mean?
In this mail service, we volition apply Apple'due south ain URLSession and its uploadTask to upload the image to server. Nosotros volition use Catbox's free API to upload the image in this post.
The Catbox API endpoint is located at https://catbox.moe/user/api.php
Table of Contents
- How raw HTTP request / response looks like
- Raw HTTP asking for multipart file upload
- Boundary Cord
- Swift code to upload paradigm and other fields
You tin download the sample URLSession UploadTask Xcode projection here
How raw HTTP request / response looks like
Say you lot have a URLSession Mail service request like this :
func postRequest(){ let config = URLSessionConfiguration.default let session = URLSession(configuration: config) let url = URL(string: "http://httpbin.org/anything")! var urlRequest = URLRequest(url: url) urlRequest.httpMethod = "POST" // your post request data permit postDict : [String: Any] = ["proper name": "axel", "favorite_animal": "play a trick on"] baby-sit let postData = effort? JSONSerialization.data(withJSONObject: postDict, options: []) else { return } urlRequest.httpBody = postData permit task = session.dataTask(with: urlRequest) { data, response, error in ... } task.resume() } The code above will generate and send a raw HTTP request (which the backend server will see and receive) like this :
This is the HTTP request that the backend server will receive from the mobile app. To effigy out how to write URLSession code which upload file and other parameters to server, we tin take the raw HTTP request and reverse engineer it. In the next department we volition discuss how to get the raw HTTP request.
Raw HTTP request for multipart file upload
According to Catbox'due south API documentation, to upload a file to their server, we volition demand to send the parameters below :
File Uploads
reqtype="fileupload" fileToUpload=(file data hither) Before we start to work on the image upload code, allow's take a look at how the HTTP request looks similar for the file upload.
The API documentation has a cURL example :
cURL to API
scroll -F "reqtype=fileupload" -F "userhash=####" -F "fileToUpload=@Asriel.png" https://catbox.moe/user/api.php gyre is a control line tool and library for transferring information with URLs — roll official website
cURL is bundled along the macOS, this post won't become into detail of cURL but information technology is a command line tool to simplify making HTTP request.
We will use Charles Proxy to sniff the HTTP request. It cost $fifty just you tin can use its free trial version without death date (just a minor timer badgerer when yous open the app), information technology will be one of the best 50 dollars yous spent in your iOS dev career. Y'all tin keep reading without downloading the app as we will explain how information technology works below.
Charles Proxy will perform a Man-in-the-eye set on on the network request (installing a root document and alter the chain of trust). Here is a very simplified diagram on how the attack works :
To intercept the request, we will brand a cURL request to the API in the Terminal using the command below :
curl --proxy 127.0.0.1:8888 --insecure -F "reqtype=fileupload" -F "userhash=caa3dce4fcb36cfdf9258ad9c" -F "fileToUpload=@Asriel.png" https://catbox.moe/user/api.php You can change the directory to a binder with epitome, then supplant the "@Asriel.png" to "@[Your image filename]", the @ indicates a local file.
The --proxy flag is used to instruct that the HTTP request generated past cURL to laissez passer through the Charles Proxy so we can intercept it.
The --insecure flag is used to tell coil to ignore any modification made to the SSL Chain of Trust. As Charles Proxy creates a simulated Root Certificate and modify the chain of trust, curl will know the chain of trust has been modified and stop the request. The --insecure flag is to tell curl to ignore the modification of chain of trust and let the request pass through.
The -F flag is used to indicate a multipart form post information, this is similar to when you submit a form with file upload in web browser (eg: https://www.w3schools.com/html/tryit.asp?filename=tryhtml_input_file)
Later on running the control, you will see it outputs the link of the uploaded paradigm (eg: https://files.catbox.moe/uz9di8.png)
Hither is the raw HTTP request data captured by Charles Proxy :
Boundary String
Notice that at that place is a separator between each of the field (reqtype, userhash and fileToUpload), the separator is the ----------582f7ab074da5615 string. This is called purlieus in HTTP terms, this is used to divide different field name and value. The boundary string has to be specified in the HTTP header content-type field.
When browsing some website, you might notice that the URL looks like https://example.com?orderID=2&userID=5 , the & in the URL is used to separate the orderID and userID, boundary works similarly. This Stack Overflow Answer explains what is purlieus pretty well.
The boundary cord tin can be set to any custom string as long as it doesn't appear anywhere else in the raw HTTP request data, so that server won't get confused on where to divide the data.
Every bit the boundary cord used by curl contains a lot of dashes in front, it might exist confusing to explain, I volition utilise another example.
Say your chosen purlieus string is FLUFFY_ES, yous volition demand to utilise --FLUFFY_ES (2 dashes padded in front) to separate different field/value.
And at the end of the HTTP request, yous will need to put --FLUFFY_ES-- (2 dashes in front and 2 dashes at the back) to indicate the end of the HTTP request data.
This is co-ordinate to the specification of HTTP 1.1.
Swift code to upload image and other fields
To produce the raw HTTP request output shown previously, we volition need to manually craft the raw data and employ it in URLSessionUploadTask. The code is every bit below :
// the image in UIImage type guard let prototype = tmpImage else { render } let filename = "avatar.png" // generate boundary string using a unique per-app string allow boundary = UUID().uuidString let fieldName = "reqtype" let fieldValue = "fileupload" let fieldName2 = "userhash" let fieldValue2 = "caa3dce4fcb36cfdf9258ad9c" let config = URLSessionConfiguration.default let session = URLSession(configuration: config) // Set the URLRequest to POST and to the specified URL var urlRequest = URLRequest(url: URL(cord: "https://catbox.moe/user/api.php")!) urlRequest.httpMethod = "POST" // Set Content-Type Header to multipart/form-data, this is equivalent to submitting form data with file upload in a spider web browser // And the boundary is also prepare here urlRequest.setValue("multipart/class-information; purlieus=\(boundary)", forHTTPHeaderField: "Content-Type") var data = Data() // Add the reqtype field and its value to the raw http asking data data.append("\r\n--\(purlieus)\r\due north".data(using: .utf8)!) data.append("Content-Disposition: course-data; name=\"\(fieldName)\"\r\n\r\n".information(using: .utf8)!) data.suspend("\(fieldValue)".data(using: .utf8)!) // Add together the userhash field and its value to the raw http reqyest data information.append("\r\n--\(purlieus)\r\due north".data(using: .utf8)!) data.suspend("Content-Disposition: course-data; name=\"\(fieldName2)\"\r\n\r\n".data(using: .utf8)!) information.suspend("\(fieldValue2)".data(using: .utf8)!) // Add together the image data to the raw http request data data.suspend("\r\northward--\(boundary)\r\n".data(using: .utf8)!) data.suspend("Content-Disposition: form-data; name=\"fileToUpload\"; filename=\"\(filename)\"\r\n".data(using: .utf8)!) data.append("Content-Type: paradigm/png\r\northward\r\n".data(using: .utf8)!) data.append(UIImagePNGRepresentation(image)!) // Terminate the raw http request data, note that there is two extra dash ("-") at the terminate, this is to indicate the end of the data // According to the HTTP 1.1 specification https://tools.ietf.org/html/rfc7230 data.append("\r\n--\(boundary)--\r\n".data(using: .utf8)!) // Send a POST request to the URL, with the data we created earlier session.uploadTask(with: urlRequest, from: data, completionHandler: { responseData, response, error in if(fault != nil){ print("\(error!.localizedDescription)") } guard let responseData = responseData else { impress("no response information") return } if allow responseString = String(data: responseData, encoding: .utf8) { print("uploaded to: \(responseString)") } }).resume() The \r\n means new line (like you press the enter central to move to the next line), according to the definition of HTTP 1.1 specification as well (https://stackoverflow.com/questions/27966357/new-line-definition-for-http-1-1-headers).
We convert the cord into information class using the "string".data(using: .utf8)! method, this will catechumen the string into data type using UTF8 encoding.
We then apply the office UIImagePNGRepresentation(epitome) to convert the UIImage into PNG data class.
In that location yous have it! Uploading images in iOS might seem circuitous at commencement, but one time you lot sympathise the concept of boundary and under the hood it is but generating the correct HTTP request for server, it becomes manageable.
You can download the sample URLSession UploadTask Xcode projection here
Source: https://fluffy.es/upload-image-to-server/
0 Response to "Swift 4 Upload Image to Php Server"
Post a Comment