しょぼしょぼプログラマ雑記

自分のための備忘録。 開発記録など掲載してます。 質問・ご意見・ご感想等はTwitter ( https://twitter.com/yun_hokuto ) までお気軽にどうぞ。

iOSとPHP間での画像のやりとりについて

iPhoneで画像を選択するライブラリはかなり転がっています

とても使いやすいですね

また、PHPも画像の操作はGD以外にもライブラリが転がってます

とても便利ですね

ただ、この2つを組み合わせたときに超が付くほどハマったので記述しておきます

やりたいこと

iPhone(iPad)で撮影・保存した画像をWebサーバーに送り、サーバー側でファイル保存を行う

たったこれだけです

仕組み

iPhone(iPad)のフォトライブラリから画像を選択

②WebサーバーにあるPHPに対してPOSTする

PHPが受け取った画像データをファイル保存

こういう流れです

仕組みの具体的な説明

iPhone(iPad)の画像選択には ELCImagePickerController というライブラリを使用

組み込みには CocoaPods を使用します

※CocoaPodsのインストールは こちらをご参照ください

ライブラリの使い方等は省略しますが、画像データをNSData(いわゆるバイナリ)にします

そしてWebサーバーへPOSTします

iPhone
// 画像をバイナリ化
UIImage *image = 画像データ;
NSData *data = UIImagePNGRepresentation(image);

// URLと転送メソッドを指定(メソッドの指定をしないとGETになります)
NSURL *url = [NSURL URLWithString:"http://example.com/upload.php"];
NSMutableURLRequest *request = [[NSMutableRequest alloc] initWithURL:url];
request.HTTPMethod = @"POST";

// パラメータを設定
request.HTTPBody = [[NSString stringWithFormat:@"image=%@", data] dataUsingEncoding:NSUTF8StringEncoding];

// サーバーに接続
NSError *error;
NSURLResponse *response;
NSData *result = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
PHP
<?php
$image = $_POST['image'];

$imgDir = "img";
$fileName = "image.png";

$fp = fopen($imgDir."/".$fileName, "wb");
@fwrite($fp, hex2bin($image));
fclose($fp);
問題点

さて、iPhoneはバイナリデータを送る、PHPはバイナリデータをそのまま書き出す

一見してよさそうに見えますが、実はこれが罠でした

作成された画像は破損していて見れないのです

iPhoneのコンソールに出力されるバイナリデータとPHPで画像をバイナリ化したときのデータを見比べます

iPhone側コンソール
<12345678 90abcedf 12345678 90abcdef ...

PHPデバッグログ

1234 5678 90ab cedf 1234 5678 90ab cdef ...

「区切り桁数はエディタによって異なるのでこれは良い」

「最初も最後もバイナリは同じ」

そういう先入観に完全に支配されていました

感の良い方はもうお気づきでしょう

NSDataはバイナリではなくバイナリ文字列であるということに・・・。

そうでないとPOSTできませんよね・・・OTL

そしてもう一つ

iPhoneはコンソール出力されたものをそのまま送っているということです

もうお気づきの方も多いのではないでしょうか?

答えは簡単

バイナリ文字列に0~9、a~f以外は存在しないということです

もうわかりますね

つまり 「<」「>」「半角スペース」が邪魔をしていたのです

というわけで、PHP側を修正

<?php
$image = trim(str_replace(array("<", ">", ' '), '', $_POST['image']));

$imgDir = "img";
$fileName = "image.png";

$fp = fopen($imgDir."/".$fileName, "wb");
@fwrite($fp, hex2bin($image));
fclose($fp);

これで無事に画像が保存できました。

めでたしめでたし


追伸

「NSDataをBase64にすればいいんじゃね?」という意見が出てきそうなので先手をば

NSDataには既に「<」「>」「半角スペース」が入っています

なので、それらを含んだ文字列をBase64するということになり、結果として画像データになりません

また、バイナリ文字列をBase64エンコードすると、文字数が増え、結果としてネットワークトラフィック量を増やします

そのため今回はバイナリ文字列をそのまま送っています