Ymka
Ymka

Reputation: 81

Encode/decode image to base64 flask&python

I have image that is called 'image.png', first off i make request to my server to save image as base64 string in json file.

def get_img_content(coding='utf-8'):
    with open('image.png', 'rb') as f:
        img_data = base64.b64encode(f.read()).decode(coding)
        return img_data

requests.get(f'http://127.0.0.1:5000/?face={get_img_content()}')

Bellow i've added server's code which handle my request

from flask import Flask, request
import os
import json

app = Flask(__name__)

if "data.json" not in os.listdir(os.getcwd()):
    with open('data.json', 'a', encoding='utf-8') as f:
        pass
    content = {'ids': [], 'results': []}
else:
    with open('data.json', 'r', encoding='utf-8') as f:
        content = json.loads(f.read())

def add_id(id, face, content=content):
    content['ids'].append({id: face})
    with open('data.json', 'w', encoding='utf-8') as f:
        f.write(json.dumps(content))

@app.route('/', methods=['GET'])
def main():
    face = request.args.get('face')
    if not face:
        return 'Valid request has to contain face'
    id = [int(list(i.keys())[0]) for i in content['ids']]
    id = str(max(id) + 1 if id else 1)
    add_id(id, face)
    print(content)
    return id


@app.route('/get', methods=['GET'])
def get():
    id = request.args.get('id')
    if not id:
        return 'Valid request has to contain id'
    for i in content['results']:
        if list(i.keys())[0] == id:
            return json.dumps({'result': i[id]})
    return "Couldn't find result"


if __name__ == '__main__':
    app.run()

After that i tried to read this base64 string from data.json and convert it to image, but i got PaddingError
binascii.Error: Incorrect padding

import base64
import json

with open('data.json', 'r', encoding='utf-8') as f:
    content = json.loads(f.read())


def to_image():
    with open('image.png', 'wb') as f:
        f.write(base64.b64decode(list(content['ids'][0].items())[-1][-1].encode('utf-8')))


to_image()

Please help me to reproduce my steps without this error.

Upvotes: 0

Views: 2409

Answers (1)

Alfredo Maussa
Alfredo Maussa

Reputation: 545

INTRODUCTION

Each format has (should have) their own "header" and "ending" signal in order to know:

  1. what type of information it contain
  2. where the data start.
  3. where it end.

See the "header" and "ending" structure for .jpg files here: JPG Signature Format: Documentation & Recovery Example

Why that exist? It prevent, exactly what happen here... there should be a "hidden" code that check if the "header" and the "ending" bytes are right, if they don't, then the data have lost. Then alert to you with:

binascii.Error: Incorrect padding

DEBUG

The arrays of bytes that you write in data.json are much less than the original array of bytes from the image... you are losing information.

OBSERVATION

What i see and i thing the problem is, is that you are sending a large data with HTTP verb GET. It has 8KB limit capacity. then if you image is larger than that you can't send it with GET request.

See the limit here: Maximum length of HTTP GET request

RECOMMENDATION

For this case, use POST request to send data larger than 8KB (*the use of POST request is not limited only because the size, there are other cases too)

SOLUTION

There are multiple ways to send that with POST request, see here: MIME types.

To send:

  1. one image, use the MIME type image
  2. multiples files with multipart/form-data
  3. you could send JSON with application/json
  4. send raw text with text/plain

See: How are parameters sent in an HTTP POST request?

Image: Image or graphical data including both bitmap and vector still images as well as animated versions of still image formats such as animated GIF or APNG. Common examples are image/jpeg, image/png, and image/svg+xml.

You can take the data, this way (I think, this is only for multiple files multipart/form-data):

face=request.files['face'].read()

Maybe the others are:

request.form['face']
request.data['face']
request.get_data['face']
request.get_json['face']

Here are the correct and differents ways to receive the data from POST request: Get the data received in a Flask request

See additional:Flask HTTP methods, handle GET & POST requests

Upvotes: 1

Related Questions