2014-10-02 3 views
58

Я пытаюсь загрузить изображение с параметрами в 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") 
    } 
} 

редактировать 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 
} 
+0

В чем проблема с 'filePathKey', я столкнулся с той же проблемой.? –

+0

Загрузить изображение просто ... [Использование Alamofire] (http://stackoverflow.com/questions/32366021/alamofire-error-code-1000-the-url-does-not-point-to-a-file-url/ 36863066 # 36863066) –

ответ

113

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

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

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

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

Для получения более подробной информации см. RFC 2388. Во всяком случае, в 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) 
     } 
    } 
} 

Или в Swift 2:

/// 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 NSURLRequest that was created 

func createRequest (userid userid: String, password: String, email: String) -> NSURLRequest { 
    let param = [ 
     "user_id" : userid, 
     "email" : email, 
     "password" : password] // build your dictionary however appropriate 

    let boundary = generateBoundaryString() 

    let url = NSURL(string: "https://example.com/imageupload.php")! 
    let request = NSMutableURLRequest(URL: url) 
    request.HTTPMethod = "POST" 
    request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type") 

    let path1 = NSBundle.mainBundle().pathForResource("image1", ofType: "png") as String! 
    request.HTTPBody = createBodyWithParameters(param, 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 NSData of the body of the request 

func createBodyWithParameters(parameters: [String: String]?, filePathKey: String?, paths: [String]?, boundary: String) -> NSData { 
    let body = NSMutableData() 

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

    if paths != nil { 
     for path in paths! { 
      let url = NSURL(fileURLWithPath: path) 
      let filename = url.lastPathComponent 
      let data = NSData(contentsOfURL: url)! 
      let mimetype = mimeTypeForPath(path) 

      body.appendString("--\(boundary)\r\n") 
      body.appendString("Content-Disposition: form-data; name=\"\(filePathKey!)\"; filename=\"\(filename!)\"\r\n") 
      body.appendString("Content-Type: \(mimetype)\r\n\r\n") 
      body.appendData(data) 
      body.appendString("\r\n") 
     } 
    } 

    body.appendString("--\(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. 

func generateBoundaryString() -> String { 
    return "Boundary-\(NSUUID().UUIDString)" 
} 

/// Determine mime type on the basis of extension of a file. 
/// 
/// This requires MobileCoreServices framework. 
/// 
/// - 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. 

func mimeTypeForPath(path: String) -> String { 
    let url = NSURL(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 NSMutableData { 

    /// Append string to NSMutableData 
    /// 
    /// Rather than littering my code with calls to `dataUsingEncoding` to convert strings to NSData, and then add that data to the NSMutableData, this wraps it in a nice convenient little extension to NSMutableData. This converts using UTF-8. 
    /// 
    /// - parameter string:  The string to be added to the `NSMutableData`. 

    func appendString(string: String) { 
     let data = string.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: true) 
     appendData(data!) 
    } 
} 

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

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 error == nil else { 
     // handle error here 
     print(error!) 
     return 
    } 

    // if response was JSON, then parse it 

    do { 
     let responseDictionary = try JSONSerialization.jsonObject(with: data!) 
     print("success == \(responseDictionary)") 

     // 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 
     // } 
    } catch { 
     print(error) 

     let responseString = String(data: data!, encoding: .utf8) 
     print("responseString = \(responseString)") 
    } 
} 
task.resume() 

Или для Swift 2 исполнение:

let request = createRequest(userid: userid, password: password, email: email) 

let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, response, error in 
    if error != nil { 
     // handle error here 
     print(error) 
     return 
    } 

    // if response was JSON, then parse it 

    do { 
     if let responseDictionary = try NSJSONSerialization.JSONObjectWithData(data!, options: []) as? NSDictionary { 
      print("success == \(responseDictionary)") 

      // note, if you want to update the UI, make sure to dispatch that to the main queue, e.g.: 
      // 
      // dispatch_async(dispatch_get_main_queue()) { 
      //  // update your UI and model objects here 
      // } 
     } 
    } catch { 
     print(error) 

     let responseString = NSString(data: data!, encoding: NSUTF8StringEncoding) 
     print("responseString = \(responseString)") 
    } 
} 
task.resume() 

Мой первоначальный ответ ниже для исторических целей:


несколько наблюдений:

  1. Вы устанавливаете HTTPBody быть стандартный формат POST (как если бы это был application/x-www-form-urlencoded запрос, даже если вы никогда не указано, что). Затем вы можете отказаться от этого и заменить его двоичными данными PNG-представления изображения. Вы предположительно хотели отправить обоих.

  2. Мы не можем посоветовать вам разъяснение по поводу того, что именно сервер ожидает, но часто это multipart/form-data, а не application/x-www-form-urlencoded (например, если это PHP веб-служба, он использует $_FILES переменные). Если вы пытаетесь сделать multipart/form-data, см. Это, POST multipart/form-data with Objective-C, например, как это сделать. Ясно, что это Objective-C, но это иллюстрирует основную технику.

    Примечание. Существуют и другие форматы, используемые другими веб-службами, поэтому я смущаюсь просто предположить, что это ожидает multipart/form-data. Вы должны точно подтвердить, что ожидает сервер.

Излишне говорить, что есть и другие вопросы, тоже (например, вы действительно должны указать Content-Type запроса, по крайней мере, вы действительно не должны выдавать синхронный запрос (если вы уже делаете это в фоновом потоке), я бы посоветовал NSURLSession и т. д.).

Но основная проблема заключается в том, как вы заселяетесь HTTPBody. Тем не менее, нам тяжело помочь вам без большей ясности в отношении того, что требует сервер.

+0

yess! Я использую $ _File! что я должен делать? – user3426109

+1

@ user3426109 Вы должны выполнить запрос 'multipart/form-data'. См. Образец кода в пересмотренном ответе – Rob

+0

Спасибо! очень полезно! – user3426109

13

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

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

Вот блог с образцом проекта, который затрагивает использовании многотомных с 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 { } 
}) 
+0

Спасибо большое, смотрел только для этого: D: D !!! –

1

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

let filename = url.lastPathComponent 

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

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

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

Thank you :)

Смежные вопросы