もくもくろぐ

技術力

SwiftでイメージをPOSTした

POSTできました。

これはなに

Swiftにて、UIImageをサーバにPOSTしたかったときに若干詰まったので報告をしておきます。
なお、HTTPリクエストを送るのには

nghialv/Net · GitHub

を使用しています。神ライブラリだ

やりたかったこと

  • UIImageをPNGに変換する
  • 適当なパラメータとともにImageをサーバに送信する
  • サーバからJSONを受け取る

Netの問題点

上記の要求に対して、

  • 画像をアップロードするのには upload() を使う
  • upload() は進捗率を返し、サーバのレスポンスを受け取らない
  • POST() はサーバのレスポンスを受け取るが、画像はアップロードできない

とう状況になっていました。二律背反!

そもそも、自分でNSURLConnectionでリクエストしたらいいんじゃないの

もちろん、自前でリクエストを送るのは何度もやっていて、 何度もやっているからこそライブラリに頼りたい。エラーハンドリングめんどくさいしね。

自前で書いたことない人はドキュメントなりブログなりをググってちゃんと調べましょう。 はまったときに死にます。

解決方法(うそ)

  • upload()を使って、パラメータは別で取得すればよい

クライアントをいじらず、サーバをいじる方法です。

そう実装できたらスマートだったんですが、初めて使う言語・フレームワークだったためよくわからず却下。

解決方法(本当)

  • POST()を使い、ヘッダを自分で設定する

コードを見た方が早い

class func sendImage(image: UIImage, to: String, success: Success, failure: Failure) {
    let path = "/post"
    let imageData = NetData(pngImage: image, filename: "test")
    let myName = User.getMe()!.name
    let params = ["data": imageData, "from": myName, "to": to]

    let boundary = "NET-UPLOAD-boundary-\(arc4random())-\(arc4random())"
    let paramsData = NetHelper.dataFromParamsWithBoundary(params, boundary: boundary)
    let header = ["Content-Length": "\(paramsData.length)", "Content-Type": "multipart/form-data; boundary=\(boundary)"]
    let net = Net(baseUrlString: "http://localhost", header: header)
    net.POST(path, params: params, successHandler: success, failureHandler: failure)
}

上の4行はpathとparameterを設定しています。
下5行はヘッダを設定してリクエストを送っています。

要は

  • imageDataとparamsを送る時は、 Content-Type、Content-Length を設定すればよい

なんというか、NSURLConnection で実装するときは当たり前でしょ?
せっかくコードを読んだのでbondaryのStringと、パラメータをNSDataに変換するところはNetから拝借しました。

ハマったとこ

  • UIImageをNSDataに変換する部分

上のコードの let imageData = のところですね。 最初は普通に

let imageData =  UIImagePNGRepresentation(image)

としていたのですが、どうもサーバに送られていないっぽい。
Net のコードを読むと、parameterにImageが含まれるかどうかを if value is NetData で判定していることがわかりました。
なので、

let imageData = NetData(pngImage: image, filename: "適当なファイル名")

としておわり。
イニシャライザの引数としては、pngImage, jpegImage があった

ライブラリのコードを読め