MongoDBにHTTPリクエストで画像バイナリデータを格納する(Micropython)

# はじめに
今回，ESP32から画像ファイルをMongoDBに格納したかったのですが，初心者であるが故，実装に苦労したので備忘録というか知識の整理として記事を書きます．
こんなこと他に誰がやるんだって感じなので需要があるか分かりませんが...

# やりたいこと
大雑把な図ですが，やりたいこととしては以下です．
![data_flow.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2747695/bcdaa8f4-1bbc-9155-9fc6-31feda688231.png)

ESP32(送信側)からESP32(受信側)に画像バイナリデータを送信し，さらにESP32(受信側)はVM上にあるMongoDBにHTTPリクエストを使って画像データを送信したいわけです．MongoDBに画像をそのまま保存できるのか分からなかったので今回はバイナリ化してテキストとして保存します．

ESP32間でのやりとりはソケット通信です．

# 実装
ソケット作成等の部分は割愛します．ここでは画像データ送信の前処理とHTTPリクエストの部分のみを記述します．

## ESP32(送信側)での処理

```python:esp32_1.py
def sending():
    global port
    global socket_to_KH
    global address
    global message
    global s

    while True:
        connection, client = s.accept()

        with open('material.jpeg', 'rb') as image_file:
            image_data = image_file.read()
        try:
            print(f'----Client {client} connected----')

            image_size = len(image_data)
            send_count = (image_size // buffer_size) + 1

            connection.send(str(send_count).encode())

            for i in range(send_count):
                start = i * buffer_size
                end = min((i + 1) * buffer_size, image_size)
                chunk = image_data[start:end]
                connection.sendall(chunk)

            print('Number of chunks:', send_count)
            utime.sleep(2)
        finally:
            connection.close()
            print('connection is closed')
            break
```
バッファサイズを超えてソケット通信で送信使用とするとうまくいかないので，送信ファイルサイズをバッファサイズで分割します．一度チャンクに分割してsendall()で送信することで送信に成功します．

## ESP32(受信側)の処理

```python:esp32_2.py
def receiving():
    global host
    global port
    global socket_to_KM
    global address
    global message

    host = wifi.ifconfig()[2]
    try:
        sendSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        red.on()
        sendSocket.connect((host,port))
        red.off()
        sendSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)


        data = sendSocket.recv(buffer_size)
        send_count = int(data.decode())
        print('Number of chunks:', send_count)

        image_data = b""
        for i in range(send_count):
            chunk = sendSocket.recv(buffer_size)
            image_data += chunk
        
        with open('received_image.jpg', 'wb') as image_file:
            image_file.write(image_data)

        print("Image received and saved as 'received_image.jpg'.")

        print(f'received message: {message}')
        sendSocket.close()
    except Exception as e:
        print(e)
        pass
```
こちらは受け取ったチャンクを次々image_dataに突っ込んでいきます．これでESP32間での画像データのやり取りができました．

次にHTTPリクエストでMongoDBに格納します．
```python:esp32_2.py
def mkb():
    with open('received_image.jpg', 'rb') as image_file:
        image_data = image_file.read()
    return image_data
```
バイナリデータ化して文字列にします．

```python:esp32_2.py
def toServer():
    image_data = mkb()
    url = 'http://192.168.***.***:5000/data'
    files = {'image':('received_image.jpg',image_data)}
    print(files)

    response = urequests.post(url, json=files)
    if response.status_code == 201:
        print('Data saved successfully')
    else:
        print('Failed to save data.')
    response.close()
```
バイナリ化したデータをimage_dataとします．
urlはmongoDBのデータコレクションを指定しています．
filesは送信する画像データのバイナリデータです．

micropythonなのでurequestsでpostします．レスポンスステータスコードを受け取って確認できるようにしています．

# おわりに
これでHTTPリクエストでESP32からMongoDBにデータを格納できました．今回は画像データを送信するとしていますが，あまり大きなサイズになるとチャンクに分割していても送信できなくなるということがありました．