I'm trying to figure out how to send a photo from an iPhone to my web server.
I also need to send parameters containing the size of the photo, it's filename and other additional information about the photo in the same request as the parameter data.
The code below is on the right track I think, but where do I put the parameter data called params
:
let params: Array<String> = [aI.filename, String(aI.size), String(aI.dateTime.year), String(aI.dateTime.month), String(aI.dateTime.day), String(aI.dateTime.hour), String(aI.dateTime.minute), String(aI.dateTime.second), String(aI.dateTime.millisecond)]
var serverURL = URL(string: "http://192.168.0.23/upload.php");
var req = NSMutableURLRequest(url: serverURL!, cachePolicy: NSURLRequest.CachePolicy.useProtocolCachePolicy, timeoutInterval: 60.0);
//Set request to post
req.httpMethod = "POST";
//Set content type
req.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type");
let task = URLSession.sharedSession().dataTaskWithRequest(req){ data, response, error in
if error != nil{
print("Error -> \(error)")
return
}
do {
let result = try NSJSONSerialization.JSONObjectWithData(data!, options: []) as? [String:AnyObject]
print("Result -> \(result)")
} catch {
print("Error -> \(error)")
}
}
task.resume()
return task
Allthough some of the answers pushed me in the right direction, they still didn't fit my project and so I continued googling an I managed to find exactly what I needed in the following article: http://swiftdeveloperblog.com/image-upload-example/
I needed to make the HTTP request asynchronously and using sessions,
which I didn't specify in the question because the question was merely about how to send both several parameters along with data in one single request.
It is called Multipart Form Data
when doing so.
I had to modify the code from the article a little bit to make it work for my application,
so I'm sharing my Swift 3 code below:
Trigger code
let params = [
"filename" : chunkOwner.filename ,
"size" : String(describing: chunkOwner.size) ,
"year" : String(chunkOwner.dateTime.year) ,
"month" : String(chunkOwner.dateTime.month) ,
"day" : String(chunkOwner.dateTime.day) ,
"hour" : String(chunkOwner.dateTime.hour) ,
"minute" : String(chunkOwner.dateTime.minute) ,
"second" : String(chunkOwner.dateTime.second) ,
"millisecond" : String(chunkOwner.dateTime.millisecond) ,
]
uploadChunk(url: URL(string: "http://192.168.0.23/upload.php")!, data: photoData, params: params)
Upload code:
func uploadData(url: URL, data: Data!, params: [String: String])
{
let cachePolicy = NSURLRequest.CachePolicy.reloadIgnoringLocalCacheData;
let request = NSMutableURLRequest(url: url, cachePolicy: cachePolicy, timeoutInterval: 6.0);
request.httpMethod = "POST";
let boundary = generateBoundaryString()
request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
if(data == nil) { return; }
request.httpBody = createBodyWithParameters(parameters: params, filePathKey: "file", data: data, boundary: boundary)
//myActivityIndicator.startAnimating();
let task = URLSession.shared.dataTask(with: request as URLRequest) {
data, response, error in
if error != nil {
print("error=\(error)")
return
}
// You can print out response object
print("******* response = \(response)")
// Print out reponse body
let responseString = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)
print("****** response data = \(responseString!)")
do {
let json = try JSONSerialization.jsonObject(with: data!, options: []) as? NSDictionary
print(json)
}catch
{
//if you recieve an error saying that the data could not be uploaded,
//make sure that the upload size is set to something higher than the size
print(error)
}
}
task.resume()
}
func createBodyWithParameters(parameters: [String: String]?, filePathKey: String?, data: Data!, boundary: String) -> Data {
var body = Data();
if parameters != nil {
for (key, value) in parameters! {
body.appendString(string: "--\(boundary)\r\n")
body.appendString(string: "Content-Disposition: form-data; name=\"\(key)\"\r\n\r\n")
body.appendString(string: "\(value)\r\n")
}
}
let mimetype = "text/csv"
body.appendString(string: "--\(boundary)\r\n")
body.appendString(string: "Content-Disposition: form-data; name=\"\(filePathKey!)\"; filename=\"\(parameters!["filename"]!)\"\r\n")
body.appendString(string: "Content-Type: \(mimetype)\r\n\r\n")
body.append(data)
body.appendString(string: "\r\n")
body.appendString(string: "--\(boundary)--\r\n")
return body
}
func generateBoundaryString() -> String {
return "Boundary-\(NSUUID().uuidString)"
}
Also include the following code at the bottom of your .swift
file outside of your class:
extension Data {
mutating func appendString(string: String) {
append(string.data(using: .utf8)!)
}
}
And for the PHP upload script I did some changes and now looks like this:
<?php
$target_dir = "/var/www/html/uploads";if(!file_exists($target_dir)){
mkdir($target_dir, 0777, true);
}
$target_dir = $target_dir . "/" . basename($_FILES["file"]["name"]);
echo count("size: ".$_FILES["file"]["tmp_name"]);
if (move_uploaded_file($_FILES["file"]["tmp_name"], $target_dir)){
echo json_encode([
"Message" => "The file ". basename( $_FILES["file"]["name"]). " has been uploaded.",
"Status" => "OK",
]);
} else {
echo json_encode([
"Message" => "Sorry, there was an error uploading your file.",
"Status" => "Error",
]);
}
?>
Important Note:
Your app will fail to upload data if your server php file called
php.ini
is configured to accept files smaller than the data you're
trying to upload.
For example: If php.ini
is configured to accept 2 MB, then any
uploads larger than 2 MB will be ignored and your app will receive a
response saying that something went wrong.
To change the file size acceptance in php.ini
you need to look for
the variable called upload_max_filesize
and post_max_size
and change those to whatever file size
your system requires.
You can put them to httpBody or to httpBodyStream (by using NSInputStream)
But don't forget to transform params for server protocol (for example xml, json, or binary data with custom format).
For your content type (application/x-www-form-urlencoded), you can find format in wikipedia:
keyName=value&keyName2=value2
The keys and values should contain of URLPathAllowedCharacterSet, to achieve it you can use stringByAddingPercentEncodingWithAllowedCharacters.
To convert the KeyValue string to NSData, you can use method dataUsingEncoding.
I am sharing you one way of posting data using NSURLConnection
in Swift3
Your URL
var serverURL = URL(string: "http://192.168.0.23/upload.php")
Your parameters to be like this , just discuss with server people to which parameters you to need pass data Then assign your value to that parameter like below
serverparameter1 = \(value to post)& serverparameter2 = \(value to post2).......
With your params I did like this have a look
let params = "filename= \(aI.filename)&size = \(String(aI.size))& dateTimeYear =\(String(aI.dateTime.year))&dateTimeMonth =\(String(aI.dateTime.month))& dateTimeDay =\(String(aI.dateTime.day))&dateTimeHour =\(String(aI.dateTime.hour))&dateTimeMinute =\(String(aI.dateTime.minute))&dateTimeSecond =\(String(aI.dateTime.second))&dateTimeMilliSecond=\(String(aI.dateTime.millisecond))"
Convert your Photo Data to Base64String like below
var base64String: NSString!
let myImage = UIImage(named:"image.png")
let imageData = UIImageJPEGRepresentation(myImage, 0.9)
base64String = imageData!.base64EncodedString(options: NSData.Base64EncodingOptions.endLineWithLineFeed) as NSString!
print(base64String)
then pass as stringParameter
&ImageDataStr = \(base64String)
then final Url seems to be look like
\(serverURL)/\(params)
OR
\(serverURL)/Upload?\(params)
Step by step request
var serverURL = URL(string: "http://192.168.0.23/upload.php")
let params = "filename= \(aI.filename)&size = \(String(aI.size))& dateTimeYear =\(String(aI.dateTime.year))&dateTimeMonth =\(String(aI.dateTime.month))& dateTimeDay =\(String(aI.dateTime.day))&dateTimeHour =\(String(aI.dateTime.hour))&dateTimeMinute =\(String(aI.dateTime.minute))&dateTimeSecond =\(String(aI.dateTime.second))&dateTimeMilliSecond=\(String(aI.dateTime.millisecond))&photoDataStr = \(base64String)"
var status:NSString = "\(serverURL)/Upload?\(params)" as NSString
status = status.addingPercentEscapes(using: String.Encoding.utf8.rawValue)! as NSString
let url = URL(string: status as String)!
let request = URLRequest(url: url, cachePolicy:NSURLRequest.CachePolicy.reloadIgnoringLocalAndRemoteCacheData, timeoutInterval: 600)
// need synchronous
Here you will get responseData
var response:URLResponse?
var responseD:Data = try! NSURLConnection.sendSynchronousRequest(request, returning:&response)
Finally make that BinaryData to readable
// save to string - the result came from the Server call
var serverResults:NSString = NSString(data: responseD, encoding: String.Encoding.utf8.rawValue)!
print(serverResults)
For Example your Result
if serverResults.range(of: "RESULT>APPROVED").location != NSNotFound
{
return "Data posted"
}
else
{
return "Failed to post"
}