Загрузить изображение с параметрами в Swift

Я пытаюсь загрузить изображение с параметрами в Swift. Когда я пытаюсь выполнить этот код, я могу получить параметры, но не изображение

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:

Я думаю, что у меня есть некоторые проблемы с пути к сохраненному UIImage, потому что php сообщает мне, что файл уже существует, и я думаю, потому что я отправляю его пустым

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
}

Ответ 1

В своем комментарии ниже вы сообщаете нам, что используете синтаксис $_FILES для получения файлов. Это означает, что вы хотите создать запрос multipart/form-data. Процесс в основном:

  1. Укажите границу для вашего запроса multipart/form-data.

  2. Укажите Content-Type запроса, который указывает, что он multipart/form-data и какова граница.

  3. Создайте тело запроса, разделяя отдельные компоненты (каждое из опубликованных значений, а также между каждой загрузкой).

Для получения более подробной информации см. RFC 7578. В любом случае, в Swift 3 и более поздних версиях это может выглядеть так:

/// 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 path1 = Bundle.main.path(forResource: "image1", ofType: "png")!
    request.httpBody = try createBody(with: parameters, filePathKey: "file", paths: [path1], 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 paths:        The optional array of file paths 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]?, filePathKey: String, paths: [String], boundary: String) throws -> Data {
    var body = Data()

    if parameters != nil {
        for (key, value) in parameters! {
            body.append("--\(boundary)\r\n")
            body.append("Content-Disposition: form-data; name=\"\(key)\"\r\n\r\n")
            body.append("\(value)\r\n")
        }
    }

    for path in paths {
        let url = URL(fileURLWithPath: path)
        let filename = url.lastPathComponent
        let data = try Data(contentsOf: url)
        let mimetype = mimeType(for: path)

        body.append("--\(boundary)\r\n")
        body.append("Content-Disposition: form-data; name=\"\(filePathKey)\"; filename=\"\(filename)\"\r\n")
        body.append("Content-Type: \(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)"
}

/// Determine mime type on the basis of extension of a file.
///
/// This requires 'import MobileCoreServices'.
///
/// - parameter path:         The path of the file for which we are going to determine the mime type.
///
/// - returns:                Returns the mime type if successful. Returns 'application/octet-stream' if unable to determine mime type.

private func mimeType(for path: String) -> String {
    let url = URL(fileURLWithPath: path)
    let pathExtension = url.pathExtension

    if let uti = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, pathExtension as NSString, nil)?.takeRetainedValue() {
        if let mimetype = UTTypeCopyPreferredTagWithClass(uti, kUTTagClassMIMEType)?.takeRetainedValue() {
            return mimetype as String
        }
    }
    return "application/octet-stream"
}

С:

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)
        }
    }
}

Имея все это, теперь вам нужно отправить этот запрос. Я бы посоветовал это сделать асинхронно. Например, используя URLSession, вы должны сделать что-то вроде:

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()

Для передач Swift 2 см. Предыдущую редакцию этого ответа.

Ответ 2

Теперь AlamoFire поддерживает Multipart:

https://github.com/Alamofire/Alamofire#uploading-multipartformdata

Вот сообщение в блоге с образцом проекта, касающимся использования Multipart с AlamoFire.

http://www.thorntech.com/2015/07/4-essential-swift-networking-tools-for-working-with-rest-apis/

Соответствующий код может выглядеть примерно так (при условии, что вы используете AlamoFire и SwiftyJSON):

func createMultipart(image: UIImage, callback: Bool -> Void){
    // use SwiftyJSON to convert a dictionary to JSON
    var parameterJSON = JSON([
        "id_user": "test"
    ])
    // JSON stringify
    let parameterString = parameterJSON.rawString(encoding: NSUTF8StringEncoding, options: nil)
    let jsonParameterData = parameterString!.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: true)
    // convert image to binary
    let imageData = UIImageJPEGRepresentation(image, 0.7)
    // upload is part of AlamoFire
    upload(
        .POST,
        URLString: "http://httpbin.org/post",
        multipartFormData: { multipartFormData in
            // fileData: puts it in "files"
            multipartFormData.appendBodyPart(fileData: jsonParameterData!, name: "goesIntoFile", fileName: "json.txt", mimeType: "application/json")
            multipartFormData.appendBodyPart(fileData: imageData, name: "file", fileName: "iosFile.jpg", mimeType: "image/jpg")
            // data: puts it in "form"
            multipartFormData.appendBodyPart(data: jsonParameterData!, name: "goesIntoForm")
        },
        encodingCompletion: { encodingResult in
            switch encodingResult {
            case .Success(let upload, _, _):
                upload.responseJSON { request, response, data, error in
                    let json = JSON(data!)
                    println("json:: \(json)")
                    callback(true)
                }
            case .Failure(let encodingError):
                callback(false)
            }
        }
    )
}

let fotoImage = UIImage(named: "foto")
    createMultipart(fotoImage!, callback: { success in
    if success { }
})

Ответ 3

Спасибо @Rob, ваш код работает нормально, но в моем случае я возвращаю изображение из галереи и беру имя изображения с помощью кода:

let filename = url.lastPathComponent

Но этот код, отображающий расширение изображения как .JPG(заглавными буквами), но сервер не принимает расширения в capital письме, поэтому я изменил свой код как:

 let filename =  (path.lastPathComponent as NSString).lowercaseString

и теперь мой код работает нормально.

Спасибо:)