Best way to upload multiple images in a single batch - ios

I was trying to upload multiple images in a single batch where I convert images into base64 strings, append them into a json and send. But when I append more than 3 or 4 images server gets null for json for some reason. I think it is because base64 strings are huge in length.
My requirement is like this, I want to upload multiple images which are under one upload id and each and every image should have its own tags.
My json was like this
{
"upload_id":[{
"tag1.tag2": "base64 string of image 1"
},
{
"tag3.tag4": "base64 string of image 2"
}]
}
and this is the code i used to make the json and upload
// Images are saved in the documents directory, image paths are saved in imgPathsArr array and tags are saved in tagsArr.
var uploadDict = [String: Any]()
var imgDictArr = [[String: Any]]()
for (indx,imgPath) in imgPathsArr.enumerated(){
do{
let imgData = try Data(contentsOf: URL(fileURLWithPath: imgPath))
let base64Str = imgData.base64EncodedString(options: .init(rawValue: 0))
let imageDict = [tagsArr[index]:base64Str]
imgDictArr.append(imageDict)
}catch let error{
print(error.localizedDescription)
}
}
uploadDict[nic!] = imgDictArr
Alamofire.request("url", method: .post, parameters: uploadDict, encoding: JSONEncoding.default, headers: nil).response { (response) in
//things after getting response
}
Any possible way to get my work done. Any help would be highly appreciated.

Multipart is better than sending it in pure binary-to-text like base64. In the multipart request, clients construct request to send files and data over to an HTTP Server. It is commonly used by browsers and HTTP clients to upload files to the server.
There is a link below which goes into Multipart a bit more in detail:
https://stackoverflow.com/a/19712083/7461562

Related

Alamofire.uploadmultipartFormData with JSONEncoding.default (Swift)

i have to send photo and json to server.
my json is :
{"anticorona":"Anti_Covid","time":"Time","navigateds":[{"collection_public_key":"Origin_Station.collection_public_key","station_public_key":"Origin_Station.public_key"},{"station_public_key":"Des_Station.public_key","collection_public_key":"Des_Station.collection_public_key"}],"seats":"Seats","date":"Date"}
how can i send this json with Alamofire.uploadmultipartFormData
i know i can use encoding: JSONEncoding.default in Alamofire.request but can in use JSONEncoding.default when use Alamofire.uploadmultipartFormData?
thanks
You seem to be new and it would be nice if you could perhaps add some code you have tried in future questions of yours. Anyways, as far as I can see this should be possible in the following way. I am assuming the key "navigateds" remains the same. Otherwise you could also check if the value (in the for-in-loop) is an array, too:
// set parameters for request
let antiCoronaParameters: Parameters = [
"anticorona" : "Anti_Covid",
"time":"Time",
"navigateds":[
["collection_public_key":"Origin_Station.collection_public_key", "station_public_key":"Origin_Station.public_key"],
["station_public_key":"Des_Station.public_key", "collection_public_key":"Des_Station.collection_public_key"]
],
"seats":"Seats",
"date":"Date"
]
let upload = AF.upload(multipartFormData: { (formData) in
// I would append file data here first
for (key, value) in antiCoronaParameters {
if key == "navigateds" {
do {
let arrayData = try JSONSerialization.data(withJSONObject: value, options: .prettyPrinted)
formData.append(arrayData, withName: key)
} catch {
print("could not append array, failed with error:", error)
}
} else if let string = value as? String, let stringData = string.data(using: String.Encoding.utf8, allowLossyConversion: false) {
formData.append(stringData, withName: key)
} else {
print("could not append some data in parameters")
}
}
}, to: "https://www.yourURLhere.com/link.php", method: .post).validate()
upload.responseString { (responseString) in
print(responseString)
}
This answer is based on this question. In the future, I would recommend trying lots of keywords relating to your question. Initially, searching for questions will consume more time but you will get the hang of it soon enough I'm sure. Just keep trying a little more next time maybe ;)
However, as you already mentioned there's another way, too, and I prepared it for you if you would like to check it out. You essentially mentioned it already and I'd always prefer it over the upload unless there's a very good reason:
// set parameters for request
let antiCoronaParameters: Parameters = [
"anticorona" : "Anti_Covid",
"time":"Time",
"navigateds":[
["collection_public_key":"Origin_Station.collection_public_key", "station_public_key":"Origin_Station.public_key"],
["station_public_key":"Des_Station.public_key", "collection_public_key":"Des_Station.collection_public_key"]
],
"seats":"Seats",
"date":"Date"
]
// request with json encoded parameters (e.g. sending to php)
let antiCoronaRequest = AF.request("https://www.yourURLhere.com/link.php", method: .post, parameters: antiCoronaParameters, encoding: JSONEncoding.default).validate()
antiCoronaRequest.responseString(completionHandler: { (response) in
print(response)
})
Hit me up if you have any questions.

download Image and save it json using SwiftyJSON

I have json in below format which I am fetching from server and parsing using SwiftyJSON.
{
name: "Ganesh"
imageURL:"www.abc.com/image.png"
}
I am downloading image using below code :
do{
let myData = try Data(contentsOf: url)
}catch{
Print("error")
}
Note: "url" contains url from json which is converted from string to URL
I want to save this "myData" in same json above with different key and access the same in future.
I am trying to save myData in json using SwiftyJSON method :
responseJSON["image"] = try JSON(data: myData)
Error which I am receiving :
"if Error while converting data into json The data couldn’t be read because it isn’t in the correct format."
I am not getting what is the problem?
Image is present at that url. if I convert myData into UIImage and If I assign it on UIImageView I can see it.
If you want to save an image in JSON, the best way would be to convert Data to Base64 string
if let base64encodedString = myData.base64EncodedString(){
responseJSON["image"] = base64encodedString
}
To restore image, try this
guard let base64encodedString = responseJSON["image"] as? String else { return }
guard let imageData = Data(base64Encoded: base64encodedString) else { return }
let image = UIImage(data: imageData)
Although Base64 - encoded images take approximately 33% more space than raw data, they are web and database safe - base64 strings contain neither control characters, nor quotes, and can be transferred as parameter in URL query strings.

"The item provided is not valid" azure mobile services invokeAPI call to backend

I'm using Azure Mobile Services in my iOS app hooked up to my backend. What I'm trying to do is upload an image to my backend as multipart/form-data.
I have done the following:
Create a "body" as NSMutableData with the multipart/form-data in it (including my image data to be uploaded)
let image_data = UIImagePNGRepresentation(image)!
let body = NSMutableData()
let fname = "test.png"
let mimetype = "image/png"
//define rest of multipart/form-data with image data embedded
body.appendData("--\(boundary)\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
body.appendData("Content-Disposition:form-data; name=\"test\"\r\n\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
body.appendData("hi\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
body.appendData("--\(boundary)\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
body.appendData("Content-Disposition:form-data; name=\"file\"; filename=\"\(fname)\"\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
body.appendData("Content-Type: \(mimetype)\r\n\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
body.appendData(image_data)
body.appendData("\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
body.appendData("--\(boundary)--\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
Upload the data to my api-end point using .invokeAPI (custom api calls)
Azure.client.invokeAPI("UploadProfileImage", body: body, HTTPMethod: "POST", parameters: nil, headers: ["Content-Type": "multipart/form-data; boundary=\(boundary)"]) { (result, response, error) -> Void in
if error == nil {
print("SUCCESs! wOO")
print(result)
}else{
print("ERROR!: \(error?.localizedDescription)")
print("response: \(response)")
print("result: \(result)")
}
}
*The "boundary" variable is being received from this func:
func generateBoundaryString() -> String {
return "Boundary-\(NSUUID().UUIDString)"
}
However the error I keep getting is "The item provided is not valid". This is driving me crazy as I'm sure my multipart/form-data is correct and that I'm uploading it in the right way, I just don't understand why it won't accept the data.
Any help would be highly appreciated!

How can I process multiple links of JSON data?

The code works perfectly. The problem is that, after trying for a while, I cannot figure out how to make my program process a second link of different JSON data.
Here is my viewDidLoad where everything goes on:
override func viewDidLoad() {
super.viewDidLoad()
var err: NSError?
let urlPath: String = "https://na.api.pvp.net/api/lol/na/v1.4/summoner/by-name/" + searchFieldDataPassed + "?api_key=(removed my private api key for obvious reasons"
var url: NSURL = NSURL(string: urlPath)!
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithURL(url) { data, response, error in
// cast response as NSHTTPURLResponse and switch on statusCode if you like
if let httpResponse = response as? NSHTTPURLResponse { switch httpResponse.statusCode { case 200..<300: println("OK") default: println("Not OK") } }
// parse JSON using NSJSONSerialization if you've got data
if let jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &err) as? NSDictionary,
let include = jsonResult.objectForKey(self.searchFieldDataPassed) as? NSDictionary {
if let summLevel = include[ "summonerLevel" ] as? NSNumber {
dispatch_async(dispatch_get_main_queue()) {
self.summonerLevel.text = "\(summLevel.integerValue)"
println("summoner level: \(summLevel.integerValue)")
}
}
if let profIconId = include[ "profileIconId" ] as? NSNumber {
dispatch_async(dispatch_get_main_queue()) {
self.profileIconId.text = "\(profIconId.integerValue)"
println("profile icon id: \(profIconId.integerValue)")
}
}
if let idNum = include [ "id" ] as? NSNumber {
dispatch_async(dispatch_get_main_queue()) {
self.idNumber = idNum
println("id number: \(self.idNumber)")
}
}
}
// spawn off another network call here if you like
}
task.resume()
}
That is from my secondViewController where all the processing goes on for JSON and then is displayed.
Here is the JSON data that I'm processing (for the first JSON parsing):
{"soon2challenger":{"id":43993167,"name":"soon2challenger","profileIconId":844,"summonerLevel":30,"revisionDate":1435549418000}}
All of that works fine, now, I want to process this JSON data which actually takes the id from the first parsed JSON data and uses it in the link to process more data, which I would like to output, part of it, to the screen.
Second JSON data:
{"summonerId":43993167,"playerStatSummaries":[{"playerStatSummaryType":"AramUnranked5x5","wins":25,"modifyDate":1423007927000,"aggregatedStats":{"totalChampionKills":676,"totalTurretsKilled":20,"totalAssists":991}},{"playerStatSummaryType":"CAP5x5","wins":15,"modifyDate":1429065922000,"aggregatedStats":{"totalChampionKills":312,"totalMinionKills":4885,"totalTurretsKilled":31,"totalNeutralMinionsKilled":511,"totalAssists":216}},{"playerStatSummaryType":"CoopVsAI","wins":28,"modifyDate":1421882181000,"aggregatedStats":{"totalChampionKills":266,"totalMinionKills":2802,"totalTurretsKilled":50,"totalNeutralMinionsKilled":385,"totalAssists":164,"maxChampionsKilled":0,"averageNodeCapture":0,"averageNodeNeutralize":0,"averageTeamObjective":0,"averageTotalPlayerScore":49,"averageCombatPlayerScore":0,"averageObjectivePlayerScore":49,"averageNodeCaptureAssist":0,"averageNodeNeutralizeAssist":0,"maxNodeCapture":0,"maxNodeNeutralize":0,"maxTeamObjective":0,"maxTotalPlayerScore":49,"maxCombatPlayerScore":0,"maxObjectivePlayerScore":49,"maxNodeCaptureAssist":0,"maxNodeNeutralizeAssist":0,"totalNodeNeutralize":0,"totalNodeCapture":0,"averageChampionsKilled":0,"averageNumDeaths":0,"averageAssists":0,"maxAssists":0}},{"playerStatSummaryType":"CoopVsAI3x3","wins":15,"modifyDate":1421882181000,"aggregatedStats":{"totalChampionKills":140,"totalMinionKills":1114,"totalTurretsKilled":9,"totalNeutralMinionsKilled":449,"totalAssists":91}},{"playerStatSummaryType":"OdinUnranked","wins":1,"modifyDate":1421882181000,"aggregatedStats":{"totalChampionKills":31,"totalAssists":45,"maxChampionsKilled":10,"averageNodeCapture":4,"averageNodeNeutralize":4,"averageTeamObjective":0,"averageTotalPlayerScore":843,"averageCombatPlayerScore":268,"averageObjectivePlayerScore":575,"averageNodeCaptureAssist":3,"averageNodeNeutralizeAssist":1,"maxNodeCapture":6,"maxNodeNeutralize":7,"maxTeamObjective":2,"maxTotalPlayerScore":1468,"maxCombatPlayerScore":529,"maxObjectivePlayerScore":939,"maxNodeCaptureAssist":5,"maxNodeNeutralizeAssist":2,"totalNodeNeutralize":22,"totalNodeCapture":25,"averageChampionsKilled":5,"averageNumDeaths":5,"averageAssists":8,"maxAssists":19}},{"playerStatSummaryType":"RankedSolo5x5","wins":116,"losses":120,"modifyDate":1433630047000,"aggregatedStats":{"totalChampionKills":1699,"totalMinionKills":33431,"totalTurretsKilled":219,"totalNeutralMinionsKilled":6501,"totalAssists":1969}},{"playerStatSummaryType":"RankedTeam3x3","wins":0,"losses":0,"modifyDate":1377726216000,"aggregatedStats":{}},{"playerStatSummaryType":"RankedTeam5x5","wins":3,"losses":0,"modifyDate":1383784473000,"aggregatedStats":{"totalChampionKills":28,"totalMinionKills":636,"totalTurretsKilled":6,"totalNeutralMinionsKilled":101,"totalAssists":41}},{"playerStatSummaryType":"Unranked3x3","wins":9,"modifyDate":1421882181000,"aggregatedStats":{"totalChampionKills":90,"totalMinionKills":1427,"totalTurretsKilled":11,"totalNeutralMinionsKilled":428,"totalAssists":105}},{"playerStatSummaryType":"URF","wins":4,"modifyDate":1435024847000,"aggregatedStats":{"totalChampionKills":68,"totalMinionKills":642,"totalTurretsKilled":14,"totalNeutralMinionsKilled":182,"totalAssists":55}},{"playerStatSummaryType":"Unranked","wins":566,"modifyDate":1435549418000,"aggregatedStats":{"totalChampionKills":8419,"totalMinionKills":128213,"totalTurretsKilled":960,"totalNeutralMinionsKilled":26117,"totalAssists":7812}}]}
Heres the link of the second JSON data I want to parse (just adding it, could be useful, but not sure):
https://na.api.pvp.net/api/lol/na/v1.3/stats/by-summoner/43993167/summary?season=SEASON2015&api_key=(took-out-my-private-api-key-for-obvious-reasons)
The link doesn't work because I have to keep my api key private to myself, but the JSON data that it displays is right above the link, which is the what it would result if you were to use the link with the api key.
Just to restate, I would like to process the second part (above of this) of JSON data, but I do not understand how to process multiple links of JSON. I have the first JSON data parsed, but am unable to parse the second JSON data.
I believe Apple is deprecating NSURLConnection. Take a look at NSURLSession. Using it, you can pass in a completion block that takes three arguments: NSData?, NSURLResponse?, and NSError?. The data object contains the JSON you can pass into the JSON serializer. After that, if you need to make another network call, just call it from inside the completion block with another NSURLSession data task. Alamofire is a great framework, but sometimes you don't need everything it provides, and it adds complexity into your app that if something goes wrong or doesn't behave the way you intend/understand, you may not fully understand why. If you want to keep it simple and under your control, use NSURLSession.
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithURL(url) { data, response, error in
// cast response as NSHTTPURLResponse and switch on statusCode if you like
// parse JSON using NSJSONSerialization if you've got data
// spawn off another network call here if you like
}
task.resume() // or in Swift 2, task?.resume()
First, i would totally prefer using some common frameworks for http requests - expecially if youre new in swift. For example here with alamofire.
https://github.com/Alamofire/Alamofire
There is also a version with integrated SwiftyJSON, so you are able to parse JSON Responses very easily.
https://github.com/SwiftyJSON/Alamofire-SwiftyJSON
So if you want to make a request, use this:
Alamofire.request(.GET, "http://httpbin.org/get")
.responseJSON { (_, _, json, _) in
var json = JSON(json)
// get the id out (depends on your structure of JSON):
let id = json["id"].int
}
Now you are able to perform a second Request (with the same Code) - Read the Documentation, how to make different Requests (like with POST) and add Parameters.
If you want to use Segues, so you want to load more data from the ID in another ViewController, you can use Segues to push the data to a second ViewController, and Load the new Content from JSON when the new ViewController is initialised.
Check out this how to send data through segues:
Sending data with Segue with Swift

Upload image into asp.net web service in swift

How can I upload an image into my web service then I can save it on my server!
I have tried the below code using POST method but I got this error
(A potentially dangerous Request.Form value was detected from the client (uploadFile="<ffd8ffe0 00104a46 4...").)
func myImageUploadRequest() {
let imageData = UIImageJPEGRepresentation(myImageView, 1)
let boundary = generateBoundaryString()
var base64String = imageData.base64EncodedStringWithOptions(.allZeros)
let myUrl = NSURL(string: "http://xxxxx/UploadImageTest");
let request = NSMutableURLRequest(URL:myUrl!);
request.HTTPMethod = "POST";
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
if(imageData==nil) { return; }
var body = NSMutableData();
body.appendString("uploadFile=\(imageData)")
request.HTTPBody = body
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) {
data, response, error in
if error != nil {
println("error=\(error)")
return
}
// You can print out response object
println("******* response = \(response)")
// Print out reponse body
let responseString = NSString(data: data, encoding: NSUTF8StringEncoding)
println("****** response data = \(responseString!)")
dispatch_async(dispatch_get_main_queue(),{
});
}
task.resume()
}
And this the POST SOAP on my Asp.net web service
POST /xxxx.asmx/UploadImageTest HTTP/1.1
Host: xxxx.com
Content-Type: application/x-www-form-urlencoded Content-Length: length
uploadFile=string&uploadFile=string
The error was sending NSData to Asp.net web service.
convert the image int base64 using this code
let imageData = UIImageJPEGRepresentation(myImageView, 1)
var base64String = imageData.base64EncodedStringWithOptions(.allZeros)
then send it to web service and make sure in your web service receive String data, not byte() array.
in your web service convert the base64 into Image and save into your server.
That's it!.
I am not familiar with Swift but in Objective C I would have prepared data something like this:
....
NSMutableString* body = [NSMutableString new];
[body appendFormat:#"uploadFile=%#",base64String];
....
The reason it is erring out is pretty obvious from the error description. You might find your code is formatting your base64 image string as
uploadFile="<ffd8ffe0 00104a46 4...
The text after uploadFile= is not at all a base64 encoded string rather the string representation of NSData and that extra < at the starting of the data is being treated as a html tag in server. ASP.NET request validation does not allow such tags in your request body as security measure, to prevent code/script injection and cross site scripting.
Even if you disable request validation from server web.config or .asmx the data would still not be interpreted by server as it would still not be in valid base64 format as the server might expect.
So my advice is to frame your request properly before sending it to server and everything should work seamlessly.

Resources