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:
Phpはファイルがすでに存在することを教えてくれるので、保存されたUIImageのパスにいくつかの問題があると思います。
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
}
以下のコメントで、$_FILES
構文を使用してファイルを取得していることをお知らせします。つまり、multipart/form-data
リクエストを作成することを意味します。プロセスは基本的に次のとおりです。
multipart/form-data
リクエストの境界を指定します。
Content-Type
とその境界を指定する要求のmultipart/form-data
を指定します。
リクエストの本文を作成し、個々のコンポーネント(投稿された各値と各アップロード間)を分離します。
詳細については、 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レンディションについては、 この回答の以前のリビジョン を参照してください。
AlamoFireはMultipartをサポートするようになりました:
https://github.com/Alamofire/Alamofire#uploading-multipartformdata
これは、AlamoFireでMultipartを使用するサンプルプロジェクトのブログ投稿です。
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に感謝します。コードは正常に機能していますが、私の場合、ギャラリーからイメージを取得し、コードを使用してイメージの名前を取得しています。
let filename = url.lastPathComponent
しかし、このコードは、イメージ拡張子を.JPG(大文字)として表示しますが、サーバーは大文字の拡張子を受け入れないため、コードを次のように変更しました:
let filename = (path.lastPathComponent as NSString).lowercaseString
そして今、私のコードは正常に動作しています。
ありがとうございました :)