Upload form-data with SwiftUI and Express backend - swift

I want to upload some user data for register new users and I want to upload a profile picture. I'v tried with this code but it doesn't work, on my backend im just receiving a request with nothing like a form-data inside.
This is my swift code
func UserRegisterRequest(firstname: String, lastname: String, username: String, email: String, password: String, image: UIImage , completionHandler: #escaping (ReturnMessage) -> Void) {
let parameters = ["name": "MyTestFile123321",
"id": "12345"]
// MARK: URL Components
var components = URLComponents()
components.scheme = "https"
components.host = "my-url"
components.path = "/register"
//MARK: Create URL
guard let url = components.url else {
print("Invalid URL")
return
}
guard let mediaImage = Media(withImage: image, forKey: "profilePicture") else { return }
var request = URLRequest(url: url)
request.httpMethod = "POST"
//create boundary
let boundary = generateBoundary()
request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
//call createDataBody method
let dataBody = createDataBody(withParameters: parameters, media: [mediaImage], boundary: boundary)
request.httpBody = dataBody as Data
print(request.httpBody)
URLSession.shared.dataTask(with: request) {
data, response, error in
if let data = data {
do {
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .formatted(DateFormatter.customFormatter)
let response = try decoder.decode(ReturnMessage.self, from: data)
completionHandler(response)
}catch(let error) {
print(error.localizedDescription)
}
} else {
print("No Data")
}
}.resume()
}
func generateBoundary() -> String {
return "Boundary-\(NSUUID().uuidString)"
}
func createDataBody(withParameters params: Parameters?, media: [Media]?, boundary: String) -> Data {
let lineBreak = "\r\n"
var body = Data()
if let parameters = params {
for (key, value) in parameters {
body.append("--\(boundary + lineBreak)")
body.append("Content-Disposition: form-data; name=\"\(key)\"\(lineBreak + lineBreak)")
body.append("\(value + lineBreak)")
}
}
if let media = media {
for photo in media {
print(photo)
body.append("--\(boundary + lineBreak)")
body.append("Content-Disposition: form-data; name=\"\(photo.key)\"; filename=\"\(photo.filename)\"\(lineBreak)")
body.append("Content-Type: \(photo.mimeType + lineBreak + lineBreak)")
body.append(photo.data)
body.append(lineBreak)
}
}
body.append("--\(boundary)--\(lineBreak)")
return body
}
I don't know if this is the right way to do it but any help is welcome.
thanks for you helping me guys

Related

How can I send nil picture as a Swift Data type in to REST service in Swift?

I am trying to upload pictures(with FTP way) by REST Service. So I have a function like below:
func saveFeedBack(feedbackType: String, feedbackText: String, picture1: Data, picture2: Data, picture3: Data)
{
let parameters = [
"ofeedbackType": feedbackType,
"ofeedbackText": feedbackText
]
let oimage1 = UIImage(data: picture1)
let oimage2 = UIImage(data: picture2)
let oimage3 = UIImage(data: picture3)
guard let mediaImage1 = Media(withImage: oimage1!, forKey: "pic1") else { return }
guard let mediaImage2 = Media(withImage: oimage2!, forKey: "pic2") else { return }
guard let mediaImage3 = Media(withImage: oimage3!, forKey: "pic3") else { return }
guard let url = URL(string: "http://abc.def.com/Mobile/SSS/feedback") else { return }
var request = URLRequest(url: url)
request.httpMethod = "POST"
let boundary = generateBoundary()
request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
let dataBody = createDataBody(withParameters: parameters, media: [mediaImage1, mediaImage2, mediaImage3], boundary: boundary)
request.httpBody = dataBody
let session = URLSession.shared
session.dataTask(with: request) { (data, response, error) in
if let response = response {
print(response)
}
if let data = data
{
do
{
if let jsonResult = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String: Any]
{
//some code in here
}
}
catch
{
print(error)
}
}
}.resume()
}
Here is the Media struct:
import UIKit
struct Media {
let key: String
let filename: String
let data: Data
let mimeType: String
init?(withImage image: UIImage, forKey key: String) {
self.key = key
self.mimeType = "image/jpeg"
self.filename = "fotograf.jpg"
guard let data = image.jpegData(compressionQuality: 1) else { return nil }
self.data = data
}
}
Let's talk about this scenario: Let's say I am sending picture 1 and picture 2. But I am not sending picture 3. Of course I am getting below error because of this code: guard let mediaImage3 = Media(withImage: oimage3!, forKey: "pic3") else { return }
Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value
How can I get rid of above problem? Pictures should be optional. I mean: functions should work fine if I don't send any picture.
Here is the CreateDataBody function
func createDataBody(withParameters params: Parameters?, media: [Media]?, boundary: String) -> Data {
let lineBreak = "\r\n"
var body = Data()
if let parameters = params {
for (key, value) in parameters {
body.append("--\(boundary + lineBreak)")
body.append("Content-Disposition: form-data; name=\"\(key)\"\(lineBreak + lineBreak)")
body.append("\(value + lineBreak)")
}
}
if let media = media {
for photo in media {
body.append("--\(boundary + lineBreak)")
body.append("Content-Disposition: form-data; name=\"\(photo.key)\"; filename=\"\(photo.filename)\"\(lineBreak)")
body.append("Content-Type: \(photo.mimeType + lineBreak + lineBreak)")
body.append(photo.data)
body.append(lineBreak)
}
}
body.append("--\(boundary)--\(lineBreak)")
return body
}
Can you try this?
import UIKit
struct Media {
let key: String
let filename: String
let data: Data?
let mimeType: String
init?(withImage image: UIImage, forKey key: String) {
self.key = key
self.mimeType = "image/jpeg"
self.filename = "photo.jpg"
self.data = image.jpegData(compressionQuality: 1)
}
}
func createDataBody(withParameters params: Parameters?, media: [Media]?, boundary: String) -> Data {
let lineBreak = "\r\n"
var body = Data()
if let parameters = params {
for (key, value) in parameters {
body.append("--\(boundary + lineBreak)")
body.append("Content-Disposition: form-data; name=\"\(key)\"\(lineBreak + lineBreak)")
body.append("\(value + lineBreak)")
}
}
if media != nil {
print("media cnt: ", media!.count)
if let media = media {
for photo in media {
body.append("--\(boundary + lineBreak)")
body.append("Content-Disposition: form-data; name=\"\(photo.key)\"; filename=\"\(photo.filename)\"\(lineBreak)")
body.append("Content-Type: \(photo.mimeType + lineBreak + lineBreak)")
body.append(photo.data!)
body.append(lineBreak)
print("photo.key", photo.key)
}
}
}
body.append("--\(boundary)--\(lineBreak)")
return body
}
func saveFeedBack(feedbackType: String, feedbackText: String, picture1: Data, picture2: Data, picture3: Data)
{
var imageList : [Media] = []
let parameters = [
"ofeedbackType": feedbackType,
"ofeedbackText": feedbackText
]
if let img1 = UIImage(data: picture1) {
if img1.size.width > 0 {
if let oImg1 = Media(withImage: img1, forKey: "pic1") {
imageListe.append(oImg1)
}
}
}
if let img2 = UIImage(data: picture2) {
if img2.size.width > 0 {
if let oImg2 = Media(withImage: img2, forKey: "pic2") {
imageListe.append(oImg2)
}
}
}
if let img3 = UIImage(data: picture3) {
if img3.size.width > 0 {
if let oImg3 = Media(withImage: img3, forKey: "pic3") {
imageListe.append(oImg3)
}
}
}
guard let url = URL(string: "http://abc.des.com/Test/SSS/feedback") else { return }
var request = URLRequest(url: url)
request.httpMethod = "POST"
let boundary = generateBoundary()
request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
let dataBody = createDataBody(withParameters: parameters, media:imageList , boundary: boundary)
request.httpBody = dataBody
let session = URLSession.shared
session.dataTask(with: request) { (data, response, error) in
if let response = response {
print(response)
}
if let data = data
{
do
{
if let jsonResult = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String: Any]
{
//some code in here
}
}
catch
{
print(error)
}
}
}.resume()
}
Just try with that hopefully this will work. I have made some changes.
Model:
struct Media {
let key: String
let filename: String
let data: Data
let mimeType: String
init?(withImage image: UIImage, forKey key: String) {
self.key = key
self.mimeType = "image/jpeg"
self.filename = "fotograf.jpg"
guard let data = image.jpegData(compressionQuality: 1) else { return nil }
self.data = data
}
}
Code:
func saveFeedBack(feedbackType: String, feedbackText: String, picture1: Data, picture2: Data, picture3: Data)
{
let parameters = [
"ofeedbackType": feedbackType,
"ofeedbackText": feedbackText
]
guard let oimage1 = UIImage(data: picture1) else { return }
guard let oimage2 = UIImage(data: picture2) else { return }
guard let oimage3 = UIImage(data: picture3) else { return }
let mediaImage1 = Media(withImage: oimage1, forKey: "pic1")
let mediaImage2 = Media(withImage: oimage2, forKey: "pic2")
let mediaImage3 = Media(withImage: oimage3, forKey: "pic3")
guard let url = URL(string: "http://abc.def.com/Mobile/SSS/feedback") else { return }
var request = URLRequest(url: url)
request.httpMethod = "POST"
let boundary = generateBoundary()
request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
let dataBody = createDataBody(withParameters: parameters, media: [mediaImage1, mediaImage2, mediaImage3], boundary: boundary)
request.httpBody = dataBody
let session = URLSession.shared
session.dataTask(with: request) { (data, response, error) in
if let response = response {
print(response)
}
if let data = data
{
do
{
if let jsonResult = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String: Any]
{
//some code in here
}
}
catch
{
print(error)
}
}
}.resume()
}

Cannot upload zip file with multipart and URLSession Swift

I am experiencing a really strange issue with uploading zip file to s3... Here is my sample code and all details:
private func uploadZipFile(_ photos:URL,_ step:String,_ presignedUrlModel: PresignedUrlModel) {
var uploadingBackgroundTask = UIBackgroundTaskIdentifier(rawValue: 3)
uploadingBackgroundTask = UIApplication.shared.beginBackgroundTask(expirationHandler: {
UIApplication.shared.endBackgroundTask(uploadingBackgroundTask)
uploadingBackgroundTask = .invalid
})
let success: () -> Void = {
DispatchQueue.main.async {
UIApplication.shared.endBackgroundTask(uploadingBackgroundTask)
uploadingBackgroundTask = .invalid
}
}
let failure: (ErrorResponse) -> Void = { error in
DispatchQueue.main.async {
UIApplication.shared.endBackgroundTask(uploadingBackgroundTask)
uploadingBackgroundTask = .invalid
}
}
let boundary = "Boundary-\(UUID().uuidString)"
let uploadingRequest = URL(string: presignedUrlModel.url)!
var request = APIhelper().getMultipartRequestBody(_type: "POST", _url: uploadingRequest, boundary: boundary)
let body = self.formBackgroundPhotoData(presignedUrlModel, step, photos, boundary)
request.httpBody = body
self.session.configuration.shouldUseExtendedBackgroundIdleMode = true
let task = session.dataTask(with: request, completionHandler: { (data, response, error) in
guard let data = data else {
return
}
if let httpResponse = response as? HTTPURLResponse {
if(httpResponse.statusCode==200)
{
success()
}
else if(httpResponse.statusCode>=400 && httpResponse.statusCode<500)
{
//getting 403 errorr
let error = ErrorResponse(message:
self.errorFormatingClass.getLocalizedErrorMessage(identifier: self.errorFormatingClass.ERROR)
,
identifier: self.errorFormatingClass.ERROR)
failure(error)
}
}
}
)
task.resume()
}
I always getting 403 response:AccessDeniedInvalid according to Policy: Policy Condition failed: ["eq", "$Content-Type", "application/zip"]
​
Here is how I form data:
private func formBackgroundPhotoData(_ presignedUrlModel: PresignedUrlModel,_ step: String,_ zip: URL,_ boundary: String) -> Data {
var body = Data()
var key = "key"
var value = presignedUrlModel.key
body.append("--\(boundary)\r\n")
body.append("Content-Disposition: form-data; name=\"\(key)\"\r\n\r\n")
body.append("\(value)\r\n")
key = "AWSAccessKeyId"
value = presignedUrlModel.AWSAccessKeyId
body.append("--\(boundary)\r\n")
body.append("Content-Disposition: form-data; name=\"\(key)\"\r\n\r\n")
body.append("\(value)\r\n")
key = "signature"
value = presignedUrlModel.signature
body.append("--\(boundary)\r\n")
body.append("Content-Disposition: form-data; name=\"\(key)\"\r\n\r\n")
body.append("\(value)\r\n")
key = "policy"
value = presignedUrlModel.policy
body.append("--\(boundary)\r\n")
body.append("Content-Disposition: form-data; name=\"\(key)\"\r\n\r\n")
body.append("\(value)\r\n")
let filename = zip.lastPathComponent
guard let data = try? Data(contentsOf: zip) else {
#if DEBUG
debugPrint("backgroundupload", "dataisnull")
#endif
return Data()
}
let mimetype = mimeTypeForPath(url: zip)
body.append("--\(boundary)\r\n")
body.append("Content-Disposition: form-data; name=\"file\"; filename=\"\(filename)\"\r\n")
body.append("Content-Type: \(mimetype)\r\n\r\n")
body.append(data)
body.append("\r\n")
body.append("--\(boundary)--\r\n")
return body
}
Here is multipart request:
public func getMultipartRequestBody(_type: String, _url: URL, boundary: String) ->URLRequest
{
var request = URLRequest(url: _url)
request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
request.timeoutInterval = 300
request.httpMethod = _type
return request
}
Now, I know this tons of code, and perhaps nobody will take a look... All I am asking: please provide an example for uploading zip or images to AWS S3, without adding S3 framework for uploading files..
Our whole team does not know what to do :(

API call for Multipart/form_data Body with application/json data

I am trying to upload some data to my server.I am tring to put my data in file and upload.The request body is multipart/form_data with a single parameter named "filemessage" containing text data which has a content-type of application/json
I am not sure how to handle the application/json as content-type for inner data contained by "filemessage".
I am getting 500 internal server error
{
"erMessage": "Unknown error",
"erCode": "UnknownErCode"
}
Here is the code.
func testMultipart()
{
let path = Bundle.main.path(forResource: "testregister", ofType: "txt")
do {
let mtext = try String(contentsOfFile: path!)
let dataA = Data(mtext.utf8)
guard let url = URL(string: "MY URL") else { return false }
var request = URLRequest(url: url)
request.httpMethod = "POST"
let mKey = "filemessage"
let mFileName = "testregister"
let mMimeType = "text/plain"
let boundary = generateBoundary()
request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
let lineBreak = "\r\n"
var body = Data()
body.append("--\(boundary + lineBreak)")
body.append("Content-Disposition: form-data; name=\"\(mKey)\"; filename=\"\(mFileName)\"\(lineBreak)")
body.append("Content-Type: \(mMimeType + lineBreak + lineBreak)")
body.append(dataA)
body.append(lineBreak)
request.httpBody = dataA
let session = URLSession.shared
session.dataTask(with: request) { (data, response, error) in
if let response = response {
print(response)
}
}.resume()
}
catch(_){print("error")}
}
func generateBoundary() -> String
{
return "Boundary-\(NSUUID().uuidString)"
}

urlrequest not sending post request

Hi i am new to IOS App developement.
My code is
func sendRequest<T: Decodable>(api: String, parameters: [String: String]? = nil, outputBlock: #escaping (T) -> () ) {
guard let url = URL(string: "http://xxyyzz.com/appRegister.php") else {return}
print("hitting : -", url.absoluteString)
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
let newparam = ["name": "rr", "pass": "123456", "email": "rr#rr.com", "passConfirm":"123456"]
let httpBody = try? JSONSerialization.data(withJSONObject: newparam)
request.httpBody = httpBody
if let data = request.httpBody, let str = String(data: data, encoding: String.Encoding.utf8) {
print(str)
}
URLSession.shared.dataTask(with: request as URLRequest) { (data, response, error) in
DispatchQueue.main.async {
Indicator.shared.hideProgressView()
if let err = error {
print(err.localizedDescription)
return
}
guard let data = data else {return}
do {
let obj = String(data: data, encoding: String.Encoding.utf8)
print(obj ?? "oberrrrr")
}
}
}.resume()
}
and console printed result as per code is below
hitting : - http://xxyyzz.com/appRegister.php
{"email":"rr#rr.com","passConfirm":"123456","name":"rr","pass":"123456"}
{"error":"Please enter all fields."}
url and parameters works well on postman that means their is something missing in my code.
just to answer the problem if anyone else faces this.
this code is fine but the problem was with php web-service as the backend developer was not accepting json values as parameter instead form data was need to send.
So, two types of fix can be made here
accept json at backend by adding :-
$postdata = file_get_contents("php://input");
$request = json_decode($postdata, true);
send form data instead json
func sendRequest<T: Decodable>(api: String, parameters: [String: Any]? = nil, outputBlock: #escaping (T) -> () ) {
guard let url = URL(string: api) else {return}
print("hitting : -", url.absoluteString)
var request = URLRequest(url: url)
if let parameters = parameters {
request.httpMethod = "POST"
var postArr = [String]()
for(key, value) in parameters
{
postArr.append(key + "=\(value)")
}
let postString = postArr.map { String($0) }.joined(separator: "&")
request.httpBody = postString.data(using: .utf8)
if let data = request.httpBody, let str = String(data: data, encoding: String.Encoding.utf8) {
print(str)
}
}
URLSession.shared.dataTask(with: request) { (data, response, error) in
DispatchQueue.main.async {
Indicator.shared.hideProgressView()
if let err = error {
print(err.localizedDescription)
return
}
guard let data = data else {return}
do {
let obj = try JSONDecoder().decode(T.self, from: data)
outputBlock(obj)
} catch let jsonErr {
print(jsonErr)
}
}
}.resume()
}

POST base64 image to web API using Swift 4

I'm trying to make a call to the OCR.space API by uploading a base 64 image but I keep getting this response:
Not a valid base64 image. The accepted base64 image format is 'data:image/;base64,'
I've checked the image many times to make sure it's encoded properly in base 64 format. Am I setting up the URLRequest properly?
Here is the code:
func extractTextFromImage(_ image: CGImage) {
let bitmapRep = NSBitmapImageRep(cgImage: image)
let imageData = bitmapRep.representation(using: NSBitmapImageRep.FileType.jpeg, properties: [:])! as Data
let imageString = imageData.base64EncodedString(options: .endLineWithCarriageReturn)
let url = URL(string: "https://api.ocr.space/parse/image")!
let session = URLSession.shared
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
request.addValue("6ea787d56088957", forHTTPHeaderField: "apikey")
var parameters = ["base64image": [ "content_type": "image/jpeg", "base64": imageString]]
do {
let jsonData = try JSONSerialization.data(withJSONObject: parameters, options: [])
let theJSONText = String(data: jsonData, encoding: String.Encoding.utf8)
print("JSON string = \(theJSONText)")
request.httpBody = jsonData
} catch {
print(error.localizedDescription)
print("ERROR: Could not convert dictionary to JSON")
return
}
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("6ea787d56088957", forHTTPHeaderField: "apikey")
let task: URLSessionDataTask = session.dataTask(with: request, completionHandler: { (data: Data?, response: URLResponse?, error: Error?) -> Void in
if let response = response {
print("RESPONSE: ", response)
} else {
print("ERROR: No response")
}
if let error = error {
print("ERROR: ", error)
} else {
print("No error")
}
do {
let dictionary = try JSONSerialization.jsonObject(with: data!, options: []) as! [String: Any]
for (key, value) in dictionary {
print("Key: ", key)
print("Value: ", value)
}
if let parsedResults = dictionary["ParsedResults"] as? [[String: Any]] {
if let parsedResult = parsedResults.first {
if let parsedText = parsedResult["ParsedText"] as? String {
print("PARSED TEXT ", parsedText)
} else {
print("ERROR: Could not read parsedText")
}
} else {
print("ERROR: Could not read first element of parsedResult")
}
} else {
print("ERROR: Could not read parsedResult")
}
} catch let error {
print("ERROR: Could not serialize jSON Data: \(error.localizedDescription)")
}
})
task.resume()
}
After alot of reading and searching, this is the code that I got to work:
///
/// Extracts the text from the given image by placing a call to the OCR.space web api
///
func extractText(from image: CGImage) {
let bitmapRep = NSBitmapImageRep(cgImage: image)
let imageData = bitmapRep.representation(using: NSBitmapImageRep.FileType.png, properties: [:])! as Data
let url = URL(string: "https://api.ocr.space/parse/image")!
let session = URLSession.shared
var request = URLRequest(url: url)
request.httpMethod = "POST"
let boundary = "--------69-69-69-69-69"
request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
request.addValue("6ea787d56088957", forHTTPHeaderField: "apikey")
request.httpBody = createBody(parameters: nil, filePathKey: "file", imageDataKey: imageData, boundary: boundary)
let task = session.synchronousDataTask(urlrequest: request)
let data = task.0
let error = task.2
if data == nil {
print("ERROR: No response from OCR.space api call")
return
}
if error != nil {
print("ERROR: ", error!)
return
}
do {
let dictionary = try JSONSerialization.jsonObject(with: data!, options: []) as! [String: Any]
if let parsedResults = dictionary["ParsedResults"] as? [[String: Any]] {
if let parsedResult = parsedResults.first {
if let text = parsedResult["ParsedText"] as? String {
parsedText = text
return
} else {
print("ERROR: Could not read parsedText")
return
}
} else {
print("ERROR: Could not read first element of parsedResult")
return
}
} else {
print("ERROR: Could not read parsedResult")
return
}
} catch let error {
print("ERROR: Could not serialize jSON Data into dictionary: \(error.localizedDescription)")
return
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
///
/// Creates a the body of the url request using the given parameters
///
private func createBody(parameters: [String: String]?, filePathKey: String?, imageDataKey: Data, boundary: String) -> Data {
var body = Data();
if parameters != nil {
for (key, value) in parameters! {
body.append("--\(boundary)\r\n".data(using: String.Encoding.utf8)!)
body.append("Content-Disposition: form-data; name=\"\(key)\"\r\n\r\n".data(using: String.Encoding.utf8)!)
body.append("\(value)\r\n".data(using: String.Encoding.utf8)!)
}
}
let filename = "image.jpg"
let mimetype = "image/jpg"
body.append("--\(boundary)\r\n".data(using: String.Encoding.utf8)!)
body.append("Content-Disposition: form-data; name=\"\(filePathKey!)\"; filename=\"\(filename)\"\r\n".data(using: String.Encoding.utf8)!)
body.append("Content-Type: \(mimetype)\r\n\r\n".data(using: String.Encoding.utf8)!)
body.append(imageDataKey)
body.append("\r\n".data(using: String.Encoding.utf8)!)
body.append("--\(boundary)--\r\n".data(using: String.Encoding.utf8)!)
return body
}