play2win play2win - 3 months ago 139
Swift Question

POST base64 encoded image string to PHP

I've read through numerous posts and watched some videos.

Problem: I'm unable to POST a base64 string image to PHP server and save to MySQL database without corruption.

What I've attempted: I've attempted passing the base64 encoded string of the image just like I would pass a string or int as a POST parameter. I verified that the POST data is successfully making it to the PHP server and it is getting saved in the database. However, when I extract the base64 encoded string and decode it so the image can be rendered in the browser, it does not work. It only shows a broken image link instead of the image. It seems as though something is happening to corrupt the data in the POST process. I say this because I copied the base64 string directly from a println() statement in Xcode and pasted it into the PHP file where it was decoded and displayed perfectly. I'm able to upload base64 strings of images from my Android app perfectly fine. The problems seems to be with how I'm doing it in Swift.

After some research, I see where I may need to specify a boundary around the image parameter. I'm not positive about this though because I'm not sure if you just need a boundary specified when you send the image file directly or if you also need it when sending a base64 encoded string of the image.

Can anyone tell me if I need to set a boundary around the base64 encoded string parameter before sending it to the PHP server? Any idea what I could be doing wrong?

let image: UIImage = imgProfilePic.image!

let size = CGSizeApplyAffineTransform(image.size, CGAffineTransformMakeScale(0.1, 0.1))
let hasAlpha = false
let scale: CGFloat = 0.0 // Automatically use scale factor of main screen

UIGraphicsBeginImageContextWithOptions(size, !hasAlpha, scale)
image.drawInRect(CGRect(origin: CGPointZero, size: size))

let scaledImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()

var imageData = UIImageJPEGRepresentation(scaledImage, 0.9)
let base64String = imageData.base64EncodedStringWithOptions(.allZeros)

var cd = CoreDataUser(pstrContext: "this")

var params = "strUsername=" + cd.getUsername()
params = params + "&strPassword=" + cd.getPassword()
params = params + "&blbProfilePic=" + base64String

Rob Rob
Answer

Earlier I referred you to http://stackoverflow.com/a/14803292/1271826 which shows one fairly limited and tactical solution to the presence of the + characters in the base-64 string. This consisted of replacing the + characters with %2B.

But I notice that you're also including user name and password in your parameters. This would suggest that you really want a more generalized solution, percent escaping all of the reserved characters. If you don't do that, this wouldn't work if the password included reserved characters (such as + or &).

When submitting parameters like this, you should percent escape the values. For example, you might define a character set to be only the "unreserved" characters (as defined by RFC 3986), namely the alphanumeric characters plus -, ., _, and ~:

extension NSCharacterSet {
    class func URLQueryValueAllowedCharacterSet() -> NSCharacterSet {
        return NSCharacterSet(charactersInString: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~")
    }
}

Then you can do something like:

func percentEscapeString(string: String) -> String {
    return string.stringByAddingPercentEncodingWithAllowedCharacters(.URLQueryValueAllowedCharacterSet())!
}

Or, if you like the old CFURLCreateStringByAddingPercentEscapes function, you can do:

func percentEscapeString(string: String) -> String {
    return CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault,
        string,
        nil,
        ":/?@!$&'()*+,;=",
        CFStringBuiltInEncodings.UTF8.rawValue) as! String;
}

The previous examples are more intuitive, but this latter approach is a little more efficient.

But regardless of how you implement it, you can now use this function to percent escape your strings:

let params = "strUsername=" + percentEscapeString(cd.getUsername()) +
    "&strPassword=" + percentEscapeString(cd.getPassword()) +
    "&blbProfilePic=" + percentEscapeString(base64String)
Comments