Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Upload image with parameters in Swift

I'm trying to upload an image with parameters in Swift. When I try this code, I can get the parameters but not the image

uploadFileToUrl(fotiño:UIImage){     var foto =  UIImage(data: UIImageJPEGRepresentation(fotiño, 0.2))       var request = NSMutableURLRequest(URL:NSURL(string: "URL"))     request.HTTPMethod = "POST"      var bodyData = "id_user="PARAMETERS&ETC""       request.HTTPBody = bodyData.dataUsingEncoding(NSUTF8StringEncoding);     request.HTTPBody = NSData.dataWithData(UIImagePNGRepresentation(foto))     println("miraqui \(request.debugDescription)")     var response: AutoreleasingUnsafeMutablePointer<NSURLResponse?>=nil     var HTTPError: NSError? = nil     var JSONError: NSError? = nil      var dataVal: NSData? =  NSURLConnection.sendSynchronousRequest(request, returningResponse: response, error: &HTTPError)      if ((dataVal != nil) && (HTTPError == nil)) {         var jsonResult = NSJSONSerialization.JSONObjectWithData(dataVal!, options: NSJSONReadingOptions.MutableContainers, error: &JSONError)          if (JSONError != nil) {             println("Bad JSON")         } else {             println("Synchronous\(jsonResult)")         }     } else if (HTTPError != nil) {         println("Request failed")     } else {         println("No Data returned")     } } 

edit 2:

I think that I have some problems with the path of the saved UIImage, because php tells me that the file already exist, which I think is because I send it in blank

func createRequest (#userid: String, disco: String, id_disco: String, pub: String, foto: UIImage) -> NSURLRequest {     let param = [         "id_user"  : userid,         "name_discoteca"    : disco,         "id_discoteca" : id_disco,         "ispublic" : pub] // build your dictionary however appropriate      let boundary = generateBoundaryString()      let url = NSURL(string: "http....")     let request = NSMutableURLRequest(URL: url)     request.HTTPMethod = "POST"     request.timeoutInterval = 60     request.HTTPShouldHandleCookies = false     request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")     var imagesaver = ImageSaver()      var image = foto  // However you create/get a UIImage     let documentsPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as String     let destinationPath = documentsPath.stringByAppendingPathComponent("VipKing.jpg")     UIImageJPEGRepresentation(image,1.0).writeToFile(destinationPath, atomically: true)       self.saveImage(foto, withFileName: "asdasd22.jpg")       var path = self.documentsPathForFileName("asdasd22.jpg")       self.ViewImage.image = self.loadImageWithFileName("asdasd22.jpg")      //  let path1 = NSBundle.mainBundle().pathForResource("asdasd22", ofType: "jpg", inDirectory: path) as String!       **//path1 always crash**       println(param.debugDescription)     println(path.debugDescription)     println(boundary.debugDescription)         request.HTTPBody = createBodyWithParameters(param, filePathKey: "asdasd22.jpg", paths: [path], boundary: boundary)      println(request.debugDescription)       return request } 
like image 599
Pedro Manfredi Avatar asked Oct 02 '14 14:10

Pedro Manfredi


Video Answer


1 Answers

In your comment below, you inform us that you are using the $_FILES syntax to retrieve the files. That means that you want to create a multipart/form-data request. The process is basically:

  1. Specify a boundary for your multipart/form-data request.

  2. Specify a Content-Type of the request that specifies that it multipart/form-data and what the boundary is.

  3. Create body of request, separating the individual components (each of the posted values as well as between each upload).

For more detail, see RFC 7578. Anyway, in Swift 3 and later, this might look like:

/// Create request /// /// - parameter userid:   The userid to be passed to web service /// - parameter password: The password to be passed to web service /// - parameter email:    The email address to be passed to web service /// /// - returns:            The `URLRequest` that was created  func createRequest(userid: String, password: String, email: String) throws -> URLRequest {     let parameters = [         "user_id"  : userid,         "email"    : email,         "password" : password]  // build your dictionary however appropriate          let boundary = generateBoundaryString()          let url = URL(string: "https://example.com/imageupload.php")!     var request = URLRequest(url: url)     request.httpMethod = "POST"     request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")          let fileURL = Bundle.main.url(forResource: "image1", withExtension: "png")!     request.httpBody = try createBody(with: parameters, filePathKey: "file", urls: [fileURL], boundary: boundary)          return request }  /// Create body of the `multipart/form-data` request /// /// - parameter parameters:   The optional dictionary containing keys and values to be passed to web service. /// - parameter filePathKey:  The optional field name to be used when uploading files. If you supply paths, you must supply filePathKey, too. /// - parameter urls:         The optional array of file URLs of the files to be uploaded. /// - parameter boundary:     The `multipart/form-data` boundary. /// /// - returns:                The `Data` of the body of the request.  private func createBody(with parameters: [String: String]? = nil, filePathKey: String, urls: [URL], boundary: String) throws -> Data {     var body = Data()          parameters?.forEach { (key, value) in         body.append("--\(boundary)\r\n")         body.append("Content-Disposition: form-data; name=\"\(key)\"\r\n\r\n")         body.append("\(value)\r\n")     }          for url in urls {         let filename = url.lastPathComponent         let data = try Data(contentsOf: url)                  body.append("--\(boundary)\r\n")         body.append("Content-Disposition: form-data; name=\"\(filePathKey)\"; filename=\"\(filename)\"\r\n")         body.append("Content-Type: \(url.mimeType)\r\n\r\n")         body.append(data)         body.append("\r\n")     }          body.append("--\(boundary)--\r\n")     return body }  /// Create boundary string for multipart/form-data request /// /// - returns:            The boundary string that consists of "Boundary-" followed by a UUID string.  private func generateBoundaryString() -> String {     return "Boundary-\(UUID().uuidString)" } 

With:

extension URL {     /// Mime type for the URL     ///     /// Requires `import UniformTypeIdentifiers` for iOS 14 solution.     /// Requires `import MobileCoreServices` for pre-iOS 14 solution      var mimeType: String {         if #available(iOS 14.0, *) {             return UTType(filenameExtension: pathExtension)?.preferredMIMEType ?? "application/octet-stream"         } else {             guard                 let identifier = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, pathExtension as CFString, nil)?.takeRetainedValue(),                 let mimeType = UTTypeCopyPreferredTagWithClass(identifier, kUTTagClassMIMEType)?.takeRetainedValue() as String?             else {                 return "application/octet-stream"             }              return mimeType         }     } }  extension Data {          /// Append string to Data     ///     /// Rather than littering my code with calls to `data(using: .utf8)` to convert `String` values to `Data`, this wraps it in a nice convenient little extension to Data. This defaults to converting using UTF-8.     ///     /// - parameter string:       The string to be added to the `Data`.          mutating func append(_ string: String, using encoding: String.Encoding = .utf8) {         if let data = string.data(using: encoding) {             append(data)         }     } } 

Having all of this, you now need to submit this request. I would advise this is done asynchronously. For example, using URLSession, you would do something like:

let request: URLRequest  do {     request = try createRequest(userid: userid, password: password, email: email) } catch {     print(error)     return }  let task = URLSession.shared.dataTask(with: request) { data, response, error in     guard let data = data, error == nil else {         // handle error here         print(error ?? "Unknown error")         return     }          // parse `data` here, then parse it          // note, if you want to update the UI, make sure to dispatch that to the main queue, e.g.:     //     // DispatchQueue.main.async {     //     // update your UI and model objects here     // } } task.resume() 

If you are uploading large assets (e.g. videos or the like), you might want to use a file-based permutation of the above. See https://stackoverflow.com/a/70552269/1271826.


For Swift 2 renditions, see previous revision of this answer.

like image 61
Rob Avatar answered Oct 21 '22 01:10

Rob