-2

Language: Swift

TargetOS: MacOS

I need to encode an image as a base64string and upload it as a PNG using IMGUR's api using an http post request. I have another function which takes a screenshot and creates it as a CGImage object. I used the provided base64string example from their api R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7 which results in a successful 200 status code. Unfortunately replacing this string with my imageToBase64String() function (below) results in an error 415 status code. I am able to display the Base64 image string in HTML without any issues.

Function to convert CGImage screenshot into Base64String:

func imageToBase64String(_ cgImage: CGImage) -> String? {
    let imageRep = NSBitmapImageRep(cgImage: cgImage).representation(using: NSBitmapImageRep.FileType.png, properties: [:])
    let base64Image:String = ((imageRep!.base64EncodedString()))
    Logger.write(base64Image)
    return base64Image
}

Here is the anonymous upload for imgur:

func anonymousUpload(_ image: CGImage){
    let base64Image = imageToBase64String(image)!

    // Using this works fine
    //let base64Image = "R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"

    let url = "https://api.imgur.com/3/upload"
    let request = NSMutableURLRequest(url: NSURL(string: url)! as URL)
    request.httpMethod = "POST"
    request.setValue("Client-ID \(imgurAPI.CLIENT_ID)", forHTTPHeaderField: "Authorization")
    let bodyString = "image=\(base64Image)&type=base64&title=sample&description=Desktop screenshot"
    request.httpBody = bodyString.data(using: .utf8)

    let task = URLSession.shared.dataTask(with: request as URLRequest){ data, response, error in
        if (error != nil){
            print("error: \(error)")
            return
        }
        print("response: \(response!)")
        let responseString = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)
        print("response string: \(responseString!)")

    }
    task.resume()
}
Potion
  • 785
  • 1
  • 14
  • 36
  • @ElTomato imagePNGRepresentation seems to be part of UIKit for IOS. Is there a solution for MacOS using CGImage? – Potion Mar 31 '19 at 02:13
  • Some more updates: I took the converted base64 data and displayed it in HTML without any issues using below: `` Ive tried to only upload the base64 data only and with the appended "data:image/png;base64," but imgur responds with `Status: 415; Code: 1003; Message: File type invalid` – Potion Apr 02 '19 at 04:19
  • Why are you using `options: .lineLength64Characters`? I cannot find any description that you need it, in the API doc of imgur. – OOPer Apr 02 '19 at 05:55
  • Your code makes no sense. How can you combine NSImage with UIImagePNGRepresentation? One is for macOS, the other is for iOS. – matt Apr 02 '19 at 14:23
  • @OOPer I found some example on reddit which used that. I thought it was standard https://www.reddit.com/r/iOSProgramming/comments/4enrvf/uploading_anonymously_to_the_imgur_api/ – Potion Apr 02 '19 at 14:55
  • @matt Sorry, I updated my post to show where I got the code that made UIImagePNGRepresentation compatible for macOS – Potion Apr 02 '19 at 14:56
  • 1
    Ok but your code is still crazy. You start with a CGImage and you twice wrap it in an NSImage only to unwrap it again. The CGImage is the image data. You don’t need to keep wrapping it like that. – matt Apr 02 '19 at 15:09
  • @matt sorry. I was initially unsure which to use but that definitely clears things up a lot. @OOper I removed the options portion and now recieve a `Status: 400 Error: Invalid URL`. Its odd because in the request I set type=base64 and not URL. – Potion Apr 02 '19 at 15:23
  • Update: I got it working for base64 (see post below). It works also for direct file upload without doing base64 conversion by pointing the file's URL and calling `NSImage.init(contentsOf: file)`. My request's body was off by a lot. I also corrected wraping from cgImage->nsimage->cgImage issue. Thanks for the feedback @matt. New to swift so I was really confused which to use. – Potion Apr 02 '19 at 18:30

1 Answers1

2

It looks like I constructed the body incorrectly. Below is the proper solution how to do so. I closely followed Postmans' generated http code to construct the request below:

func anonymousUpload(_ image: CGImage){

    // Convert the file to base64
    let base64Image:String = imageToBase64String(image)!

    // Create our url
    let url = URL(string: "https://api.imgur.com/3/image")!
    let request = NSMutableURLRequest.init(url: url)
    request.httpMethod = "POST"
    request.addValue("Client-ID " + imgurAPI.CLIENT_ID, forHTTPHeaderField: "Authorization")

    // Build our multiform and add our base64 image
    let boundary = NSUUID().uuidString
    request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
    let body = NSMutableData()
    body.append("--\(boundary)\r\n".data(using: .utf8)!)
    body.append("Content-Disposition: form-data; name=\"image\"\r\n\r\n".data(using: .utf8)!)
    body.append(base64Image.data(using: .utf8)!)
    body.append("\r\n".data(using: .utf8)!)
    body.append("--\(boundary)--\r\n".data(using: .utf8)!)
    request.httpBody = body as Data

    // Begin the session request
    let task = URLSession.shared.dataTask(with: request as URLRequest){ data, response, error in
        if (error != nil){
            print("error: \(error)")
            return
        }

        print("response: \(response!)")
        let responseString = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)
        print("response string: \(responseString!)")

    }
    task.resume()
}
Potion
  • 785
  • 1
  • 14
  • 36