質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.35%

  • Python

    9119questions

    Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

  • Python 3.x

    7328questions

    Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

  • Unity

    4380questions

    Unityは、ユニティテクノロジーが開発したゲームエンジンです。 主にモバイルやブラウザ向けのゲーム製作に利用されていましたが、3Dの重力付きゲームが簡単に作成できることから需要が増え、現在はマルチプラットフォームに対応しています。 言語はC言語/C++で書かれていますが、C#、JavaScript、Booで書かれたコードにも対応しています。

バイナリデータが含まれるbyte型のstring変換でエラーが発生する

解決済

回答 4

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 2,347

suvera

score 89

前提・実現したいこと

今までUnity(クライアント)からのリクエストは文字列だけを扱っていて
Unityでjsonの文字列をバイト型に変換したものをリクエストで送信していました。

Python(サーバー)は、受け取ったリクエストのバイト型を
文字列(string)に直し、json.loads()で扱いやすい形にしてから扱うと言った流れで進めていました。

今回起きた問題は画像のデータを扱うことで発生した問題です。
Unity側は画像をバイナリデータで送信するために新しく
WWWFormAddBinaryData()を扱うことになりました。

そして、Python側で送られてきたバイト型のデータを
今まで通り文字列に変換しようとしたところでエラーが発生しました。

stringに変換している理由はjson.loads()がbytesには対応していないからです。

画像はこの後AWSのS3にアップロードしたいため
バイナリか、画像データで扱える状態にしたいです。

発生している問題・エラーメッセージ

エラーは上記コードの
body = body.decode("utf-8")
行で発生しています。

UnicodeDecodeError: 'utf-8' codec can't decode byte 0x89 in position 139: invalid start byte

該当のソースコード

Unity(画像データを送信する側)

WWWForm form = new WWWForm;
form.AddBinaryData("testimage", data, null, null);
uging(UnityWebRequest www = UnityWebRequest.Post(url,form)) {
  www.SetRequestHeader("Content-Type", "application/json");
  yirld return www.Send();
}

Python(画像データを受け取る側)

def on_post(self, req, res):
    print("POST")
    # postパラメーターを取得
    body = req.stream.read()

    # bytes型で受け取るので、文字列に変換する。
    if type(body) == bytes:
        body = body.decode("utf-8")
    data = json.loads(body)

補足情報(言語/FW/ツール等のバージョンなど)

Mac
Unity5.5,

AWS EC2インスタンス
Nginx
gunicorn
Python3.5
falcon

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • turbgraphics200

    2017/02/22 16:59 編集

    回答に変更しました。

    キャンセル

回答 4

+1

JSON の仕様(RFC 7159)上、バイナリデータを直接扱えません。
JSON では文字列として「UTF-8, UTF-16, UTF-32」のいずれかでなくてはならず、UTF-8 以外は実装の制限で処理されない可能性もあります。

ですので、バイナリデータを渡したければ、バイナリを Base64 変換するなどして、UTF-8 で表現しなおさなくてはなりません。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/02/22 14:31

    ありがとうございます。
    Unityから送信する際にBase64 を利用して送信してみます。

    キャンセル

checkベストアンサー

0

受け取る側が json を期待するなら送る側も json で送ればいいんじゃないですか?
Base64 を使いましょう。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/02/22 14:31

    ありがとうございます。
    Unityから送信する際にBase64 を利用して送信してみます。

    キャンセル

0

バイナリーデータを受け取る方法はありますが、その前にちょっと質問なのですが、今までもWWWを使用してきたのですか?それと、これはネットワーク対戦のようなものですか?

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/02/23 11:44

    今まではJsonの文字列をバイトにしたものを送るような形で渡していました。

    キャンセル

  • 2017/02/23 11:47 編集

    ちょっと具体的にお聞きしたいのが、このサーバーに送るデータはどのようなデータなのでしょう。ネットワーク対戦のように頻繁にアクセスするものなのでしょうか。それとも戦績などといったそれほど頻繁にアクセスする必要のないデータなのでしょうか。

    キャンセル

  • 2017/02/23 12:24

    そこまで頻繁ではないはずです。
    一クライアントなら一日に数回あったら多い方です。
    ただ、アクセスするクライアント数は多くなる予定です。

    キャンセル

  • 2017/02/23 12:25

    その程度ですか。了解です。

    キャンセル

  • 2017/02/23 12:35

    すいませんがあともう一つ質問。サーバーに送ったデータはUnityから取得するのでしょうか、それともウェブなどのべつのプラットフォームで公開するといった使い方なのでしょうか?

    キャンセル

0

一応、UnityからPython(Falcon)に画像を添付してJSONデータを送信するコードです。

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEngine;

public class posttest : MonoBehaviour {

    // JSONとして送るデータのクラス
    [Serializable]
    class SendData
    {
        public int UserId;
        public string UserName;
    }

    // Use this for initialization
    void Start () {
        // JSONで送るデータ
        var userData = new SendData();
        userData.UserId = 300;
        userData.UserName = "Piyo";

        // 送信する画像数が一定でない場合は配列で扱うほうがいいでしょう。
        // Resourcesフォルダ配下にある画像ファイルパスを取得
        string[] extensions = { "bmp", "gif", "jpg", "jpeg", "png", "tif" };
        string[] imageFiles = Directory.GetFiles(Path.Combine(Application.dataPath, "Resources"), "*.*")
            .Where(f => f.EndsWith(".bmp") || f.EndsWith(".gif") || f.EndsWith(".jpg") || f.EndsWith(".jpeg") || f.EndsWith(".png") || f.EndsWith(".tif")).ToArray();


        StartCoroutine(uploadData(userData, imageFiles));
    }

    // POST送信メソッド
    IEnumerator uploadData(SendData userData, string[] uploadImages)
    {

        var userDataJson = JsonUtility.ToJson(userData);
        var formData = new WWWForm();
        formData.AddField("data", userDataJson);
        for(var i = 0; i < uploadImages.Length; i++)
        {
            FileInfo fileInfo = new FileInfo(uploadImages[i]);
            var binData = File.ReadAllBytes(uploadImages[i]);
            formData.AddBinaryData("img" + i, binData, fileInfo.Name);
            //binData = null;
        }

        var w = new WWW("http://localhost:8080", formData);
        yield return w;
        if (!string.IsNullOrEmpty(w.error))
        {
            print(w.error);
        }
        else
        {
            print("Finished Uploading Screenshot");
        }
        yield break;
    }
}
import falcon
import os
import struct
import json
from falcon_multipart.middleware import MultipartMiddleware

class HogeResource(object):

    def on_get(self, req, res, filename):
        res.status = falcon.HTTP_200
        if filename == '':
            filename = 'test.html'
        fn, ext = os.path.splitext(filename)
        ext = ext.lower()
        if ext == '.html':
            res.content_type = 'text/html'
        elif ext == '.css':
            res.content_type = 'text/css'
        elif ext == '.js':
            res.content_type = 'text/javascript'

        with open(filename, 'r') as f:
            res.body = f.read()

    def on_post(self, req, res, **kwargs):
        fidx = 0
        # JSONで送られるデータは引き続きJSONで取得
        userData = json.loads(req.params['data'][0])
        while True:
            fparam = req.get_param('img%s' % fidx)
            if fparam is not None:
                raw = fparam.file.read()
                # バイナリーデータでファイルタイプ不明の場合は自力で判定し拡張子を設定
                '''
                ext = ''      
                if raw[0] == 0x89 and raw[1] == 0x50 and raw[2] == 0x4E and raw[3] == 0x47 and raw[4] == 0x0D and raw[5] == 0x0A and raw[6] == 0x1A and raw[7] == 0x0A:
                    ext = '.png'
                elif raw[0] == 0xFF and raw[1] == 0xD8:
                    ext = '.jpg'
                elif raw[0] == 0x47 and raw[1] == 0x49 and raw[2] == 0x46:
                    ext = '.gif'
                elif raw[0] == 0x42 and raw[1] == 0x4D:
                    ext = '.bmp'
                elif (raw[0] == 0x49 and raw[1] == 0x49) or (raw[0] == 0x4D and raw[1] == 0x4D):
                    ext = '.tif'
                else:
                    ext = '.bin'
                '''
                here = os.path.dirname(os.path.realpath(__file__))
                # ファイル名はユーザー名をプレフィックスにして保存
                filepath = os.path.join(here, userData['UserName'] + '_' + fparam.filename)
                #画像ファイルとして書き出す
                with open(filepath, "wb") as f:
                    f.write(raw)
                fidx += 1
            else:
                break
        res.body = 'post!!'

app = falcon.API(middleware=MultipartMiddleware())
app.add_route('/{filename}', HogeResource())

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

同じタグがついた質問を見る

  • Python

    9119questions

    Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

  • Python 3.x

    7328questions

    Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

  • Unity

    4380questions

    Unityは、ユニティテクノロジーが開発したゲームエンジンです。 主にモバイルやブラウザ向けのゲーム製作に利用されていましたが、3Dの重力付きゲームが簡単に作成できることから需要が増え、現在はマルチプラットフォームに対応しています。 言語はC言語/C++で書かれていますが、C#、JavaScript、Booで書かれたコードにも対応しています。