Загрузить изображение с параметрами в 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
}
3 ответа:
в своем комментарии ниже вы сообщаете нам, что используете
$_FILES
синтаксис для извлечения файлов. Это означает, что вы хотите создатьmultipart/form-data
запрос. Процесс в основном:
укажите границу для вашего
multipart/form-data
запрос.указать
Content-Type
запроса, который указывает, что этоmultipart/form-data
и что такое граница.создать запрос, отделяя компоненты (каждое из размещенных значений, а также между каждой загрузкой).
Подробнее см. 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()
мой оригинальный ответ ниже для исторических целей:
пара замечаний:
вы
HTTPBody
стандартныйPOST
формат (как если бы это былоapplication/x-www-form-urlencoded
запрос, хотя вы никогда не указывали, что). Затем вы продолжаете отбрасывать это и замените его двоичными данными PNG-представления изображения. Вы, вероятно, хотели отправить обоих.мы не можем посоветовать вам с разъяснением относительно того, что именно ожидает сервер, но часто это
multipart/form-data
, а неapplication/x-www-form-urlencoded
(например, если это веб-сервис PHP, он использует$_FILES
переменной). Если вы пытаетесь сделатьmultipart/form-data
, это видеть, POST multipart / form-данные с целью-C, например, как это сделать. Ясно, что это Objective-C, но он иллюстрирует основную технику.обратите внимание, есть и другие форматы, которые используют другие веб-службы, поэтому я не решаюсь просто предположить, что это ожидает
multipart/form-data
запрос. Вы должны точно подтвердить, что сервер ожидает.разумеется, есть и другие вопросы, тоже (например, вы действительно должны указать
Content-Type
запрос, по крайней мере; вы действительно не должны выдавать синхронный запрос (если вы уже делаете это в фоновом потоке); я бы, наверное, посоветовалNSURLSession
; etc.).но главная проблема заключается в том, как вы заполняете
HTTPBody
. Однако нам трудно помочь вам дальше без большей ясности относительно того, что требуется серверу.
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 { } })
спасибо @Rob, ваш код работает нормально, но в моем случае, я переписываю изображение из gallary и беру имя изображения с помощью кода:
let filename = url.lastPathComponent
но это код, отображающий расширение изображения как .JPG (в заглавной букве), но сервер не принимает расширения в captital letter, поэтому я изменил свой код как:
let filename = (path.lastPathComponent as NSString).lowercaseString
и теперь мой код работает нормально.
спасибо :)