Convert Image to Base64

Hi, So when the user fill all his data he must select a photo from carrier (photos)


@IBAction func selecFoto(_ sender: UIButton) {
//select photo from carrier
let imageController = UIImagePickerController()
imageController.delegate = self
imageController.sourceType = UIImagePickerController.SourceType.photoLibrary
self.present(imageController, animated: true, completion: nil)
}
//display it on UIVIEW
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
image.image = info[UIImagePickerController.InfoKey.originalImage] as? UIImage
self.dismiss(animated: true, completion: nil)
}

So the user's photo is named 'image'

image converts into base64

*note this code is before the UIbutton action *

class func convertImageToBase64(image: UIImage) -> String {
let imageData = UIImagePNGRepresentation(image)!
return imageData.base64EncodedString(options: Data.Base64EncodingOptions.lineLength64Characters) }


so and sends into my database

@IBAction func crearCuenta(_ sender: UIButton) {
if nombreConductor.text == "" || email.text == "" || password.text == "" || confirmarpass.text == "" || telefono.text == "" || telefonoContacto.text == "" || emailContacto.text == "" || ciudad.text == "" || direccion.text == "" || matricula.text == "" || licencia.text == "" || numeroTaxi.text == "" || tipoVehiculo.text == "" || year.text == "" || marcaCarro.text == "" {
displayAlert(title: "Información Faltante", message: "Debes porporcionar los datos solicitados")
}
if password.text != confirmarpass.text {
displayAlert(title: "Usuario", message: "Las contraseñas no coinciden")
}
else{
let request = NSMutableURLRequest(url: NSURL(string: "hehe")! as URL)
request.httpMethod = "POST"
let postString = "Nombre_Completo=\(nombreConductor.text!)&Correo=\(email.text!)&Password=\(password.text!)&Telefono=\(telefono.text!)&Email_Contacto=\(emailContacto.text!)&Telefono_Contacto=\(telefonoContacto.text!)&Nivel=\(nivel)&Ciudad=\(ciudad.text!)&Direccion=\(direccion.text!)&Matricula=\(matricula.text!)&Licencia=\(licencia.text!)&N_Taxi=\(numeroTaxi.text!)&Tipo_Vehiculo=\(tipoVehiculo.text!)&Marca_Vehiculo=\(marcaCarro.text!)&Year=\(year.text!)&Foto=\(image!)"
request.httpBody = postString.data(using: String.Encoding.utf8)
let task = URLSession.shared.dataTask(with: request as URLRequest) {
data, response, error in
if error != nil {
print("error=\(String(describing: error))")
return
}
print("response = \(String(describing: response))")
let responseString = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)
print("responseString = \(String(describing: responseString))")
}
task.resume()
}
}


i get this response from the console


*which is true and sends sucessfully the data to the DB, but not the Image*


response = Optional( { URL: hehe } { Status Code: 200, Headers {
"Content-Encoding" = (
gzip
);
"Content-Type" = (
"text/html; charset=UTF-8"
);
Date = (
"Fri, 26 Oct 2018 18:24:15 GMT"
);
Server = (
Apache
);
"x-powered-by" = (
"PHP/5.6.38"
);
} })
responseString = Optional({"Response":"true"}) //true



and this is what my DB gets on the image Field


<UIImageView: 0x147d0d7d0; frame = (93 1502; 188 83); opaque = NO; autoresize = RM BM; userInteractionEnabled = NO; layer = <CALayer: 0x2816f2820>>


any ideas?

Answered by OOPer in 337506022

So the user's photo is named 'image'

It is not true. You are storing the user's photo into a UIImageView named `image`. And you are trying to send the UIImageView, not an image.

I strongly recommend you not to name a `UIImageView` as `image`, rename it to `imageView`:

    @IBOutlet weak var imageView: UIImageView!


//display it on UIImageView
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
imageView.image = info[UIImagePickerController.InfoKey.originalImage] as? UIImage
self.dismiss(animated: true, completion: nil)
}


*note this code is before the UIbutton action *

class func convertImageToBase64(image: UIImage) -> String {
let imageData = image.pngData()!
return imageData.base64EncodedString(options: Data.Base64EncodingOptions.lineLength64Characters)
}

Seems this is the right version of your code, but it is not true that the func converts the image as string when the user press the submit button. It is just a class method and you need to call it explicitly. Though, the option `lineLength64Characters` may not be needed.

class func convertImageToBase64(image: UIImage) -> String {
let imageData = image.pngData()!
return imageData.base64EncodedString()
}


And one more, when you post some special characters like `+` or `=` which may be included in a Base-64 string, you need to escape them.

You need something like this somewhere in your code (not inside a class):

extension String {
var urlQueryValueEscaped: String {
let urlQueryValueAllowed = CharacterSet.urlQueryAllowed.subtracting(CharacterSet(charactersIn: "+&="))
return self.addingPercentEncoding(withAllowedCharacters: urlQueryValueAllowed)!
.replacingOccurrences(of: " ", with: "+")
}
}


And then, your `crearCuenta(_:)` would be something like this:

@IBAction func crearCuenta(_ sender: UIButton) {
//### You should better use guard
guard
let nombreConductorText = nombreConductor.text, !nombreConductorText.isEmpty,
let emailText = email.text, !emailText.isEmpty,
let passwordText = password.text, !passwordText.isEmpty,
let confirmarpassText = confirmarpass.text, !confirmarpassText.isEmpty,
let telefonoText = telefono.text, !telefonoText.isEmpty,
let telefonoContactoText = telefonoContacto.text, !telefonoContactoText.isEmpty,
let emailContactoText = emailContacto.text, !emailContactoText.isEmpty,
let ciudadText = ciudad.text, !ciudadText.isEmpty,
let direccionText = direccion.text, !direccionText.isEmpty,
let matriculaText = matricula.text, !matriculaText.isEmpty,
let licenciaText = licencia.text, !licenciaText.isEmpty,
let numeroTaxiText = numeroTaxi.text, !numeroTaxiText.isEmpty,
let tipoVehiculoText = tipoVehiculo.text, !tipoVehiculoText.isEmpty,
let yearText = year.text, !yearText.isEmpty,
let marcaCarroText = marcaCarro.text, !marcaCarroText.isEmpty
else {
displayAlert(title: "Información Faltante", message: "Debes porporcionar los datos solicitados")
return
}
guard passwordText == confirmarpassText else {
displayAlert(title: "Usuario", message: "Las contraseñas no coinciden")
return
}
//### You need to convert the user's photo to Base-64 string explicitly
guard let userImage = imageView.image else {
displayAlert(title: "Usuario", message: "El foto no es selectado")
return
}
//### `ViewController` should be renamed to the class name where you defined `convertImageToBase64(image:)`
let imageBase64 = ViewController.convertImageToBase64(image: userImage)
//### You should better use `URLRequest` with `var` instead of `NSMutableURLRequest`
//### You should better use `URL` instead of `NSURL`
var request = URLRequest(url: URL(string: "hehe")!)
request.httpMethod = "POST"
//### You need to escaape POST parames properly
let postParams = [
"Nombre_Completo": nombreConductorText,
"Correo": emailText,
"Password": passwordText,
"Telefono": telefonoText,
"Email_Contacto": emailContactoText,
"Telefono_Contacto": telefonoContactoText,
"Nivel": nivel,
"Ciudad": ciudadText,
"Direccion": direccionText,
"Matricula": matriculaText,
"Licencia": licenciaText,
"Tipo_Vehiculo": tipoVehiculoText,
"Marca_Vehiculo": marcaCarroText,
"Year": yearText,
"Foto": imageBase64,
]
let postString = postParams.map {"\($0.key.urlQueryValueEscaped)=\($0.value.urlQueryValueEscaped)"}
.joined(separator: "&")
request.httpBody = postString.data(using: .utf8)
//### You have no need to use `as URLRequest`
let task = URLSession.shared.dataTask(with: request) {
data, response, error in
if let error = error {
print("error=\(error)")
return
}
print("response = \(response?.description ?? "")")
guard let data = data else {
print("Something wrong: data = nil")
return
}
//### You should better use `String` instead of `NSString`
let responseString = String(data: data, encoding: .utf8)!
print("responseString = \(responseString)")
}
task.resume()
}

Where do you call convertImageToBase64 ?


If that can help, I do this for jpeg representation (UIImage is saved on a file named fileNameJPG)


if (fileManager.fileExists(atPath: fileNameJPG)) {
if let jpegData = NSMutableData(contentsOfFile: fileNameJPG) {
encodedString = jpegData.base64EncodedString()
}
}


To convert back from string to image :

var imageIcon = UIImage()
if let encodedString = message[imageKey] as? String, let imageData = Data(base64Encoded: encodedString) {
imageIcon = UIImage(data: imageData) ?? UIImage()
}

i don't call back the image, i just store into the DB


Where do you call convertImageToBase64 ?


i don't call it i though the func converts the image as string when the user press the submit button

class func convertImageToBase64(image: UIImage) -> String {
let imageData = image.pngData()!
return imageData.base64EncodedString(options: Data.Base64EncodingOptions.lineLength64Characters)
}


so i send it as a string


let postString = "Nombre_Completo=\(nombreConductor.text!)&Correo=\(email.text!)&Password=\(password.text!)&Telefono=\(telefono.text!)&Email_Contacto=\(emailContacto.text!)&Telefono_Contacto=\(telefonoContacto.text!)&Nivel=\(nivel)&Ciudad=\(ciudad.text!)&Direccion=\(direccion.text!)&Matricula=\(matricula.text!)&Licencia=\(licencia.text!)&N_Taxi=\(numeroTaxi.text!)&Tipo_Vehiculo=\(tipoVehiculo.text!)&Marca_Vehiculo=\(marcaCarro.text!)&Year=\(year.text!)&Foto=\(image!)"
&Foto=\(image!)" // here

im doing it wrong way?

Accepted Answer

So the user's photo is named 'image'

It is not true. You are storing the user's photo into a UIImageView named `image`. And you are trying to send the UIImageView, not an image.

I strongly recommend you not to name a `UIImageView` as `image`, rename it to `imageView`:

    @IBOutlet weak var imageView: UIImageView!


//display it on UIImageView
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
imageView.image = info[UIImagePickerController.InfoKey.originalImage] as? UIImage
self.dismiss(animated: true, completion: nil)
}


*note this code is before the UIbutton action *

class func convertImageToBase64(image: UIImage) -> String {
let imageData = image.pngData()!
return imageData.base64EncodedString(options: Data.Base64EncodingOptions.lineLength64Characters)
}

Seems this is the right version of your code, but it is not true that the func converts the image as string when the user press the submit button. It is just a class method and you need to call it explicitly. Though, the option `lineLength64Characters` may not be needed.

class func convertImageToBase64(image: UIImage) -> String {
let imageData = image.pngData()!
return imageData.base64EncodedString()
}


And one more, when you post some special characters like `+` or `=` which may be included in a Base-64 string, you need to escape them.

You need something like this somewhere in your code (not inside a class):

extension String {
var urlQueryValueEscaped: String {
let urlQueryValueAllowed = CharacterSet.urlQueryAllowed.subtracting(CharacterSet(charactersIn: "+&="))
return self.addingPercentEncoding(withAllowedCharacters: urlQueryValueAllowed)!
.replacingOccurrences(of: " ", with: "+")
}
}


And then, your `crearCuenta(_:)` would be something like this:

@IBAction func crearCuenta(_ sender: UIButton) {
//### You should better use guard
guard
let nombreConductorText = nombreConductor.text, !nombreConductorText.isEmpty,
let emailText = email.text, !emailText.isEmpty,
let passwordText = password.text, !passwordText.isEmpty,
let confirmarpassText = confirmarpass.text, !confirmarpassText.isEmpty,
let telefonoText = telefono.text, !telefonoText.isEmpty,
let telefonoContactoText = telefonoContacto.text, !telefonoContactoText.isEmpty,
let emailContactoText = emailContacto.text, !emailContactoText.isEmpty,
let ciudadText = ciudad.text, !ciudadText.isEmpty,
let direccionText = direccion.text, !direccionText.isEmpty,
let matriculaText = matricula.text, !matriculaText.isEmpty,
let licenciaText = licencia.text, !licenciaText.isEmpty,
let numeroTaxiText = numeroTaxi.text, !numeroTaxiText.isEmpty,
let tipoVehiculoText = tipoVehiculo.text, !tipoVehiculoText.isEmpty,
let yearText = year.text, !yearText.isEmpty,
let marcaCarroText = marcaCarro.text, !marcaCarroText.isEmpty
else {
displayAlert(title: "Información Faltante", message: "Debes porporcionar los datos solicitados")
return
}
guard passwordText == confirmarpassText else {
displayAlert(title: "Usuario", message: "Las contraseñas no coinciden")
return
}
//### You need to convert the user's photo to Base-64 string explicitly
guard let userImage = imageView.image else {
displayAlert(title: "Usuario", message: "El foto no es selectado")
return
}
//### `ViewController` should be renamed to the class name where you defined `convertImageToBase64(image:)`
let imageBase64 = ViewController.convertImageToBase64(image: userImage)
//### You should better use `URLRequest` with `var` instead of `NSMutableURLRequest`
//### You should better use `URL` instead of `NSURL`
var request = URLRequest(url: URL(string: "hehe")!)
request.httpMethod = "POST"
//### You need to escaape POST parames properly
let postParams = [
"Nombre_Completo": nombreConductorText,
"Correo": emailText,
"Password": passwordText,
"Telefono": telefonoText,
"Email_Contacto": emailContactoText,
"Telefono_Contacto": telefonoContactoText,
"Nivel": nivel,
"Ciudad": ciudadText,
"Direccion": direccionText,
"Matricula": matriculaText,
"Licencia": licenciaText,
"Tipo_Vehiculo": tipoVehiculoText,
"Marca_Vehiculo": marcaCarroText,
"Year": yearText,
"Foto": imageBase64,
]
let postString = postParams.map {"\($0.key.urlQueryValueEscaped)=\($0.value.urlQueryValueEscaped)"}
.joined(separator: "&")
request.httpBody = postString.data(using: .utf8)
//### You have no need to use `as URLRequest`
let task = URLSession.shared.dataTask(with: request) {
data, response, error in
if let error = error {
print("error=\(error)")
return
}
print("response = \(response?.description ?? "")")
guard let data = data else {
print("Something wrong: data = nil")
return
}
//### You should better use `String` instead of `NSString`
let responseString = String(data: data, encoding: .utf8)!
print("responseString = \(responseString)")
}
task.resume()
}

Wow I have no words, thanks for responding as always and changing my code to a different way, it opens my eyes seen ways differently

starting from here

guard passwordText == confirmarpassText else {
displayAlert(title: "Usuario", message: "Las contraseñas no coinciden")
return

as a beginner, you always think about if statements as IF != to this

do {

}

but that way you write completely change my perspective to see things

if equal run, the code.. 'else' display alert.

even small things are important


Text.isEmpty didn't know about that is way simpler than

if text.text == ""


//### `ViewController` should be renamed to the class name where you defined `convertImageToBase64(image:)`

let imageBase64 = crearchoferViewController.convertImageToBase64(image: userImage)

yeah i changed as you said

// main class
class crearchoferViewController: UIViewController, UINavigationControllerDelegate, UIImagePickerControllerDelegate, UITextFieldDelegate {

extensions are confuse but very useful


and yes i notice that you try to translate that part


displayAlert(title: "Usuario", message: "El foto no es selectado")

almost accurate and thanks for that



last thing i don't know if i need to change my php code to make it work

because I'm getting this error and runs like a thousand times in console


2018-10-26 17:22:55.788863-0500 123Taxi[1398:333701] [BoringSSL] boringssl_session_errorlog(236) [C2.1:2][0x123e478a0] [boringssl_session_write] SSL_ERROR_SYSCALL(5): operation failed externally to the library



*this is only displayed once*


response = <NSHTTPURLResponse: 0x282e3b640> { URL: hehe } { Status Code: 413, Headers {

"Content-Length" = (

351

);

"Content-Type" = (

"text/html; charset=iso-8859-1"

);

Date = (

"Fri, 26 Oct 2018 22:22:56 GMT"

);

Server = (

Apache

);

} }

responseString = <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">

<html><head>

<title>413 Request Entity Too Large</title>

</head><body>

<h1>Request Entity Too Large</h1>

The requested resource<br />/hehe<br />

does not allow request data with POST requests, or the amount of data provided in

the request exceeds the capacity limit.

</body></html>

you always think about if statements as IF != to

No, not always. I use (and recommend for some others to use) `guard` in the following two conditions:

#1 The conditions in the guard-statement represent requirements to go on processing

(You want to do only a few things in its else-block, show an error, exit the processing.)

#2 If-let is not better for that context


For #2, I use if-let to handle an Optional error, on the line 65. of my code.


Text.isEmpty didn't know about that is way simpler

Some reasons I choose `if let textFieldText = textField.text, !textFieldText.isEmpty`:

#1 `if textField.text == ""` is equivalent to `if textField.text != nil && textField.text! ==""`, your if-statement does not hit when the `text` property is nil.

(The current documentation is unclear whether it always is non-nil or can be nil.)

#2 In some collection types such as Arrays, when checking if an instance is empty or not, `arr.isEmpty` is more efficient than `arr.count == 0`

(Though `arr == []` or `str == ""` may not be a bad choice. I just want to advertise the property `isEmpty`.)


almost accurate and thanks for that

Please tell me an accuate spanish message in this case. (Just for my iterest.)


And this may be the main concern of you:

<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">

<html><head>

<title>413 Request Entity Too Large</title>

</head><body>

<h1>Request Entity Too Large</h1>

The requested resource<br />/hehe<br />

does not allow request data with POST requests, or the amount of data provided in

the request exceeds the capacity limit.

</body></html>

When you get this sort of response from your PHP server, you may need to check the following settings,

usually in php.ini.


post_max_size

This needs to have a big enough size as to hold your base64-converted image, approximately 1.33 times of your binary image.


You should better check another setting to prepare to send your image as multipart/form-data.

upload_max_filesize

This needs to have a size larger than your actual binary image.

hola InvaDerZim,


Me parece que OOPer ha proporcionado una incredible respuesta. Puedes cerrar el thread en su respuesta correcta.

Please tell me an accuate spanish message in this case. (Just for my iterest.)

"Debes seleccionar la foto" -> You must select the photo(in order to continue)


i got it working!!!! i'm so excited i read alot and got deppresed tons of times trying to find what could i do

class func convertImageToBase64(image: UIImage) -> String {
let imageData = image.jpegData(compressionQuality: 0.5) // to half of quiality
return imageData!.base64EncodedString()
}


compression to 0.5 do the work


thanks for everything once again amigo 🙂

"Debes seleccionar la foto" -> You must select the photo(in order to continue)

Gracias. (I hope this is an appropriate word here.)


And I'm very happy to hear that you have made it working.

Convert Image to Base64
 
 
Q