Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

image upload using POST-API when iOS app is in background

I am trying to upload images selected by a user from their gallery, and then keep uploading them even if the user puts my app in background/suspended state and then starts using some other app.

But, along with image I want to send some dictionary parameters in httpBody (like the "Pro_Id") of the request keeping the image in multipart uploading

Background upload multiple images using single NSURLSession uploadTaskWithRequest

I have done the following as answered in Background upload multiple images using single NSURLSession uploadTaskWithRequest:

To upload in a background session, the data must first saved to a file.

  1. Save the data to file using writeToFile:options:.
  2. Call NSURLSession uploadTaskWithRequest:fromFile: to create the task. Note that the request must not contain the data in the HTTPBody otherwise the upload will fail.
  3. Handle completion in the URLSession:didCompleteWithError: delegate method.
  4. You may also want to handle uploads which complete while the app is in the background.

Implement 1. application:handleEventsForBackgroundURLSession:completionHandler in the AppDelegate. 2. Create an NSURLSession with the provided identifier. 3. Respond to the delegate methods as per a usual upload (e.g. handle the response in URLSession:didCompleteWithError:) 4. Call URLSessionDidFinishEventsForBackgroundURLSession when you have completed processing the event.

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 = "kyleleeheadiconimage234567.jpg"

        guard let data = image.jpegData(compressionQuality: 0.7) else { return nil }
        self.data = data
    }

}


@IBAction func postRequest(_ sender: Any) {
let uploadURL: String = "http://abc.xyz.com/myapi/v24/uploadimageapi/sessionID?rtype=json"

let imageParams = [
            "isEdit":"1",
            "Pro_Id":"X86436",
            "Profileid":"c0b7b9486b9257041979e6a45",
            "Type":"MY_PLAN",
            "Cover":"Y"]


guard let mediaImage = Media(withImage: UIImage(named: "5MB")!, forKey: "image") else { return }

let imageData = mediaImage.data

let randomFilename = "myImage"
let fullPath = getDocumentsDirectory().appendingPathComponent(randomFilename)

do {
   let data = try NSKeyedArchiver.archivedData(withRootObject: imageData, requiringSecureCoding: false)
   try data.write(to: fullPath)
    } catch {
   print("Couldn't write file")
    }


guard let url = URL(string: uploadURL) else { return } 
var request = URLRequest(url: url)
request.httpMethod = "POST"

let boundary = generateBoundary()

request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
        request.addValue("Client-ID f65203f7020dddc", forHTTPHeaderField: "Authorization")
request.addValue("12.2", forHTTPHeaderField: "OSVersion")
request.addValue("keep-alive", forHTTPHeaderField: "Connection")


let dataBody = createDataBody(withParameters: imageParams, media: nil, boundary: boundary)

request.httpBody = dataBody

ImageUploadManager.shared.imageUploadBackgroundTask = UIApplication.shared.beginBackgroundTask {
print(UIApplication.shared.backgroundTimeRemaining)
// upon completion, we make sure to clean the background task's status
            UIApplication.shared.endBackgroundTask(ImageUploadManager.shared.imageUploadBackgroundTask!)

ImageUploadManager.shared.imageUploadBackgroundTask = UIBackgroundTaskIdentifier.invalid

} 

let session = ImageUploadManager.shared.urlSession

session.uploadTask(with: request, fromFile: fullPath).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 {
                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
    }




class ImageUploadManager: NSObject, URLSessionDownloadDelegate {

    static var shared: ImageUploadManager = ImageUploadManager()

    var imageUploadBackgroundTask: UIBackgroundTaskIdentifier?

    private override init() { super.init()}

    lazy var urlSession: URLSession = {
        let config = URLSessionConfiguration.background(withIdentifier: "   ***My-Background-Upload-Session-*********  ")
        config.isDiscretionary = true
        config.sessionSendsLaunchEvents = true
        config.isDiscretionary = false
        return URLSession(configuration: config, delegate: self, delegateQueue: OperationQueue())//nil)
    }()


    func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
        if totalBytesExpectedToWrite > 0 {
            let progress = Float(totalBytesWritten) / Float(totalBytesExpectedToWrite)
            print("Progress \(downloadTask) \(progress)")
        }
    }
    //first this method gets called after the call coming to appDelegate's handleEventsForBackgroundURLSession method, then moves to didCompleteWithError
    func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
        print("************* Download finished ************* : \(location)")
        try? FileManager.default.removeItem(at: location)
    }

    func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
        print("************* Task completed ************* : \n\n\n \(task), error: \(error) \n************\n\n\n")

        UIApplication.shared.endBackgroundTask(self.imageUploadBackgroundTask!)
        self.imageUploadBackgroundTask = UIBackgroundTaskIdentifier.invalid

        print(task.response)

        if error == nil{
        }else{
        }

    }
}

I should be getting a success message saying that image uploaded but Instead I am getting error message in response saying please specify "pro_ID" in the request.

Is there anything wrong with my implementation? And, how do other ios apps upload images in the background, they too must be sending some data to tell to which object in the backend does that image belongs to?

like image 600
pravir Avatar asked Jul 24 '19 06:07

pravir


1 Answers

I don't think we can add httpbody, you may have to send data using query params like we send in GET request and then on the server if yours is in PHP use $_GET['Your_Id'] to access that id

like image 145
micTesting Avatar answered Oct 24 '22 12:10

micTesting