59859


Swiftでテキストデータと画像データをPHPにPOSTしたいと思っています．

テキストデータは渡せたのですが，画像データを渡すところができていません．  
※DateやUserIDなどの情報はデータベースに追加されているのを確認済みです．画像も同様に別データベースに追加されるはずですができていません．

そもそもPHPの知識があまりなく，画像データで渡さなければいけない情報がこれで合っているのかも分からない状態です．

おそらく，送る画像データに複数のパラメータも送らないといけないかと思います（type?など）

画像に関するデータのPOSTする方法が分かる方がいましたら，お教えいただければ幸いです．

**取れているデータ**

[code]

    201910182027273320
    Optional(405797 bytes)
    パラメーター：["Name": "papapa", "UserID": "pepepe", "Comment": "pooooooooo", "Platform": "Xcode", "Date": "201910182027273320", "file": Optional(<UIImage: 0x600003bce060>, {472, 384}), "type": "image/png"]
    <NSHTTPURLResponse: 0x6000010e5320> { URL: PHPのURL } { Status Code: 403, Headers {
        "Accept-Ranges" =     (
            bytes
        );
        Connection =     (
            "keep-alive"
        );
        "Content-Length" =     (
            1422
        );
        "Content-Type" =     (
            "text/html"
        );
        Date =     (
            "Fri, 18 Oct 2019 11:28:09 GMT"
        );
        "Last-Modified" =     (
            "Wed, 08 Mar 2017 06:08:47 GMT"
        );
        Server =     (
            Apache
        );
    } }
    parse error
    
    
[/code]

**Swift**

[code]

    class ViewController: UIViewController, URLSessionTaskDelegate {
    
        @IBOutlet weak var label: UILabel!
    
        @IBOutlet weak var imageView: UIImageView!
    
        var json: NSData!
    
        // 仮でPOSTする情報
        var date = "111"
        var user_id = "pepepe"
        var name = "papapa"
        var comment = "pooooooooo"
        var platform = "Xcode"
        var imageData = UIImage(named: "res2.png")
        var fileName = "image/png"
        let path = "PHPのURL"
    
        override func viewDidLoad() {
            super.viewDidLoad()
            self.getToday()
            print(date)
        }
    
        @IBAction func buttonTapped(_ sender: Any) {
            post()
        }
    
        // 日付を取得するメソッド
        func getToday(format: String = "yyyyMMddHHmmssSSSS") {
            let now = Date()
            let formatter = DateFormatter()
            formatter.dateFormat = format
            date = formatter.string(from: now as Date)
        }
    
        // PHPにPOSTする
        func post() {
    
            let client = APIClient()
            let parameters:[String: Any]  = ["file": imageData , "type": fileName, "Date": date, "UserID": user_id, "Name": name, "Comment": comment, "Platform": platform]
            client.multipartPost(urlString: path, parameters: parameters)
    
            print("パラメーター：\(parameters)")
        }
    }
    
    extension NSMutableData {
        func appendString(_ string: String) {
            let data = string.data(using: String.Encoding.utf8, allowLossyConversion: false)
            append(data!)
        }
    }
    
    class APIClient {
    
        func multipartPost(urlString: String, parameters: [String: Any]) {
    
            let url = URL(string: urlString)
            var request = URLRequest(url: url!)
            request.httpMethod = "POST"
    
            let (headers, body) = APIClient.createMultiPartPost(parameters: parameters)
    
            // ヘッダーの設定
            for header in headers {
                request.addValue(header.value, forHTTPHeaderField: header.key)
            }
    
            // Bodyの設定
            request.httpBody = body
    
            let task = URLSession.shared.dataTask(with: request) { data, response, error in
                if let data = data, let response = response {
                    print(response)
                    do {
                        let json = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.allowFragments)
                        print(json)
                    } catch {
                        print("parse error")
                    }
                } else {
                    print(error ?? "unknown error")
                }
            }
    
            task.resume()
        }
    
        static func createMultiPartPost(parameters: [String: Any]) -> (headers: [String:String], body: Data) {
    
            let uniqueId = UUID().uuidString
            let boundary = "---------------------------\(uniqueId)"
    
            let header = [
                "Content-Type" : "multipart/form-data; boundary=\(boundary)"
            ]
    
            var body = Data()
    
            let boundaryText = "--\(boundary)\r\n"
    
            for param in parameters {
    
                switch param.value {
                case let image as UIImage:
    
                    let imageData = image.pngData()
    
                    print(imageData)
    
                   // let ui8Bytes: [UInt8] = [ 0, 1, 2, 3, 4 ]
                   // let ui8Data = Data(bytes: ui8Bytes)
                   // let decodedUi8Bytes = [UInt8](imageData!)
    
                    body.append(boundaryText.data(using: .utf8)!)
                    body.append("Content-Disposition: form-data; name=\"\(param.key)\"; filename=\"\(uniqueId).png\"\r\n".data(using: .utf8)!)
                    body.append("Content-Type: image/png\r\n\r\n".data(using: .utf8)!)
    
                    body.append(imageData!.base64EncodedData())
                    body.append("\r\n".data(using: .utf8)!)
    
                case let string as String:
    
                    body.append(boundaryText.data(using: .utf8)!)
                    body.append("Content-Disposition: form-data; name=\"\(param.key)\";\r\n\r\n".data(using: .utf8)!)
                    body.append(string.data(using: .utf8)!)
                    body.append("\r\n".data(using: .utf8)!)
    
                default:
                    break
                }
            }
    
            body.append("--\(boundary)--\r\n".data(using: .utf8)!)
            return (header, body)
        }
    }
    
[/code]

**PHP**

[code]

    <?php
        class Data
        {
            public function AddComment()
            {
              // 別スクリプトからDBへの接続を一括で行う
              require_once('mysql_connect.php');
              $pdo = connectDB();
                $ID;
                $Date    = $_POST["Date"];
                $UserID  = $_POST["UserID"];
                $Name    = $_POST["Name"];
                $Comment = $_POST["Comment"];
                $Img = $_FILES["file"];
                $Platform = $_POST["Platform"];
                $UpdatedAt;
    
                try{
                    // $pdo=new PDO($dnsinfo,$USER,$PW);//接続
    
                    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);//デバックする処理
                    if($pdo==null){
                        die("error");
                    }else{
                        print("投稿用DB接続しました");
                    }
                    $stmt=$pdo->prepare("INSERT INTO 19_comment_data VALUES(?,?,?,?,?,?,?)");
                    $res=$stmt->execute(array($ID,$Date,$UserID,$Name,$Comment,$Platform,$UpdatedAt));
    
                    $sql='SELECT * FROM 19_comment_data ORDER BY id DESC LIMIT 1';
                    $stmt=$pdo->prepare($sql);
                    $stmt->execute();
                    $val= $stmt->fetchAll();
                    print($val[0]['id']);
    
                    // アップロードされたファイルがpng形式か確認　効いてなくね？
                    if ($_FILES["file"]["type"] == "image/png") {
                        if ($_FILES["file"]["error"] > 0) {
                            echo "Return Code:" . $_FILES["file"]["error"] . "";
                        } else {
                            echo "Upload:" . $_FILES["file"]["name"] . "";
                            echo "Type:" . $_FILES["file"]["type"] . "";
                            echo "Size:" . ($_FILES["file"]["size"] / 1024) . "Kb";
                            echo "Temp file:" . $_FILES["file"]["tmp_name"] . "";
    
                            print($val[0]['id']);
    
                            // サーバー上に既にファイルが存在していないか確認
                            if (file_exists("upload/" . $_FILES["file"]["name"])) {
                                echo $_FILES["file"]["name"] . "already exists.";
                            } else {
                                // pngに変換する
                                move_uploaded_file($_FILES["file"]["tmp_name"], "upload/" . $val[0]['id'].".png");
                                $image = imagecreatefrompng("upload/" . $val[0]['id'].".png");
    
                                // pngのaを使えるようにする処理
                                imagealphablending($image, false);
                                imagesavealpha($image, true);
    
                                // png画像として再保存
                                imagepng($image, "upload/" . $val[0]['id'].".png");
                                imagedestroy($image);
                                echo "Stored in:" . "upload/" . $_FILES["file"]["name"];
                            }
                        }
                    } else {
                        echo "Invalid file";
                    }
    
                    // リサイズ処理  500kb以上ならリサイズ
                    if($_FILES["file"]["size"] / 1024 > 500)
                    {
                      list($width, $hight) = getimagesize("upload/" . $val[0]['id'].".png"); // 元の画像名を指定してサイズを取得
                      $baseImage = imagecreatefrompng("upload/" . $val[0]['id'].".png"); // 元の画像から新しい画像を作る準備
                      // 正方形とかに書き出すことも可能
                      $image = imagecreatetruecolor($width/10, $hight/10); // サイズを指定して新しい画像のキャンバスを作成
    
                      // 画像のコピーと伸縮
                      imagecopyresampled($image, $baseImage, 0, 0, 0, 0, $width/10, $hight/10, $width, $hight);
    
                      // コピーした画像を出力する
                      imagepng($image ,"upload/" . $val[0]['id'].".png");
                      imagedestroy($image);
                    }
    
                }catch(PDOException $e)
                {//エラー
                    $res=$e->getMessage();
                }
    
                if($res==TRUE){
                    print("投稿success");
                }else if($res==FALSE){
                    print("投稿error");
                }
    
            }
        }
    
        $obj=new Data();
        $obj->AddComment();
    
    ?>
    
[/code]


CC BY-SA 4.0
2019-10-17T15:03:43.250
59773
2019-10-22T00:17:40.360
2019-10-21T07:56:20.210
35822
35822
question
swift php 画像
SwiftでPHPにMultiPartPostでデータを渡したい


色々情報を付け加えていただいて、問題のありそうな場所が少し絞り込めてきたので、回答の形で書かせていただきたいと思います。

まずは、確認から。

  * 現在のSwiftのコードは問題なくビルドできてシミュレータが起動する

(Xcodeやシミュレータのバージョン、何よりプラットフォームがiOSであることも記載したほうが良いでしょう。)

  * シミュレータ上でボタンを押すと、データ送信されていると思われるメッセージがデバッグコンソールに現れる。「 **取れているデータ** 」としたのは、デバッグコンソールに表示されたもの

(データの取り方には、色々あります。その取り方をどこにも書かないで、「取れているデータ」として示すより、「デバッグコンソールの表示内容」と書いてもらったほうが、Xcodeに慣れている読者には伝わりやすいです。)

  * PHP側では現在質問に表示しているコードでテストしている。上記の1回のテストで、

    1. 同時に送った「DateやUserIDなどの情報はデータベースに追加されているのを確認」

    2. 同時に送った「画像も同様に別データベースに追加されるはず」だができていない

(「テキストデータは渡せた」とだけ書かれても、どのような手順で何を確認したから「渡せた」と判断したのかがわかりません。またテキストデータを渡すだけのテストコードを作った時には動いたと言うことなのか、現在のコードでテキストと画像を送るとテキストだけが「渡せた」のかわかりません。また、「渡せた」と言うあなたの判断ではなく、最新のご質問にあるように「データベースに追加されているのを確認」と言う具体的な確認内容を記載してください。)

さて、長々と書いてきましたが、上記の前提が崩れると、考えうる問題の範囲が大きく広がってしまいますので、もし「それは違う」と言う部分があれば、お早めにお知らせください。

* * *

さて、まずはSwift側です。以前ご質問の初版に関するコメントで「大きな問題点は一箇所だけ」と書きました。その時のコードは「pngデータではなく、jpegデータを送っている」と言う大きな問題はありましたが、「画像データを送るコードとして(無駄は多いが)正しく動くはず」のものだったのに、現在はそうなっていません。

(なお、コメントをしっかり読んでそれをご質問内容の改善に生かしていただくのは大変良いことだと思うのですが、コードの修正を含む場合、どこにどんな修正を施したのか簡単にでも触れておくと、最初から質問を読んでいる読者にとって読みやすいものになります。)

あなたの現在のコードで致命的に問題があるのはこちら:

[code]

                    body.append(imageData!.base64EncodedData())
    
[/code]

なぜここでBase-64になんかしてしまっているのでしょうか?multipart/form-dataのコード例でも見かけたことはありません。Content-
Transfer-Encodingを指定すればうまくいくかもしれませんが、普通はそんなことはしません。そのまま使いましょう。

[code]

                    body.append(imageData!)
    
[/code]

これでSwift側の修正は完了です。(「致命的」でない単なる「無駄」については、長くなって本質的な問題がどこかわかりにくくなるので省略します。)このコードで正しく動かないのであれば、それは
**PHP側に問題がある** と言うことです。

* * *

あなたのPHPコードでは手がかりを掴むようなログ出力も行なっていないようなのですが、「DateやUserIDなどの情報はデータベースに追加されているのを確認済み」と言うことなので、その後に何らかの問題があるのだろうと推測できます。

[code]

                    $stmt=$pdo->prepare("INSERT INTO 19_comment_data VALUES(?,?,?,?,?,?,?)");
                    $res=$stmt->execute(array($ID,$Date,$UserID,$Name,$Comment,$Platform,$UpdatedAt));
    
    //↑INSERT文の実行までは動いている
    //↓これ以降のどこかの行(ここに示した範囲以外を含めて)に問題がある
    
                    $sql='SELECT * FROM 19_comment_data ORDER BY id DESC LIMIT 1';
                    $stmt=$pdo->prepare($sql);
                    $stmt->execute();
                    $val= $stmt->fetchAll();
                    print($val[0]['id']);
                    ...
    
[/code]

またPHP/Apacheから403というエラーコード(Forbidden)が返ってきています。これはPHP/Apacheの設定上「アクセスしちゃいけないところにアクセスしようとした」と言うのを示しています。(これだけではどこをチェックすれば良いのか見当もつきませんが、上記のようにPHP側の上に示したポイント以降を見れば良いはずです。)

そのつもりでPHPのコードを再度よく見直してみると、怪しい箇所が見えてくるのですが、できればもう少し確実な情報が欲しいところです。

Swift側の`APIClient.multipartPost(urlString:parameters:)`内の`print(response)`の後に、次のような1行を付け加えて、どんな出力が得られるか調べてみていただけないでしょうか?

[code]

                    print(response)
                    print(String(data: data, encoding: .utf8) ?? "Unknown encoding") //###
    
[/code]


CC BY-SA 4.0
2019-10-22T00:17:40.360
59859
2019-10-22T00:17:40.360
13972
59773
answer