Обработка изображений в API с помощью Python Flask / Connexion и Swagger - PullRequest
0 голосов
/ 07 ноября 2018

Я попытался настроить очень простое приложение. Я хотел создать это приложение как полноценное приложение для обучения будущих проектов. Поэтому я написал бэкэнд на python, который предоставляет данные из БД (SQLLite) через API (Flask / Connexion). API документируется через Swagger. БД должна иметь таблицу, в которой каждая строка получила 2 значения: 1. имя 2. изображения Я быстро столкнулся с проблемой: на самом деле я не знаю, как обрабатывать изображения в API. Поэтому я создал резервную копию с заполнителем. До сих пор изображения - это просто еще одна строка, которая в основном пуста. Все отлично работает Но теперь я хочу иметь возможность получать изображения через API и сохранять их в БД. Я абсолютно не представляю, как это сделать. Надеюсь, один из вас сможет мне помочь.

Вот мой Код:

SqlliteHandler.py

import sqlite3

conn = sqlite3.connect('sprint_name.db')
c = conn.cursor()


def connect_db():
    global conn
    global c
    conn = sqlite3.connect('sprint_name.db')
    c = conn.cursor()
    c.execute("CREATE TABLE if not exists sprint_names ( name text, image text)")


def make_db_call(execute_statement, fetch_smth=""):
    global c
    connect_db()
    print(execute_statement)
    c.execute(execute_statement)
    response = ""
    if fetch_smth is "one":
        response = transform_tuple_to_dict(c.fetchone())
    if fetch_smth is "all":
        response_as_tuples = c.fetchall()
        response = []
        for sug in response_as_tuples:
            response.append(transform_tuple_to_dict(sug))

    conn.commit()
    conn.close()
    return response


def transform_tuple_to_dict(my_tuple):
    return {"name": my_tuple[0], "image": my_tuple[1]}


def add_name(suggestion):
    name = suggestion.get("name")
    image = "" if suggestion.get("image") is None else suggestion.get("image")
    execute_statement = "SELECT * FROM sprint_names WHERE name='" + name + "'"
    print(execute_statement)
    alreadyexists = False if make_db_call(execute_statement, "one") is None else True
    print(alreadyexists)
    if not alreadyexists:
        execute_statement = "INSERT INTO sprint_names VALUES ('" + name + "', '" + image + "')"
        make_db_call(execute_statement)


def delete_name(suggestion_name):
    execute_statement = "DELETE FROM sprint_names WHERE name='" + suggestion_name + "'"
    print(execute_statement)
    make_db_call(execute_statement)


def delete_all():
    make_db_call("DELETE FROM sprint_names")


def get_all_names():
    return make_db_call("SELECT * FROM sprint_names", "all")


def get_name(suggestion_name):
    print(suggestion_name)
    execute_statement = "SELECT * FROM sprint_names WHERE name='" + suggestion_name + "'"
    print(execute_statement)
    return make_db_call(execute_statement, "one")


def update_image(suggestion_name, suggestion):
    new_name = suggestion.get("name" )
    new_image = "" if suggestion.get("image") is None else suggestion.get("image")
    execute_statement = "UPDATE sprint_names SET name='" + new_name + "', image='" + new_image + "' WHERE name='"\
                        + suggestion_name + "'"
    make_db_call(execute_statement)

RunBackEnd.py

from flask import render_template
import connexion

# Create the application instance
app = connexion.App(__name__, specification_dir='./')
# Read the swagger.yml file to configure the endpoints
app.add_api('swagger.yml')

# Create a URL route in our application for "/"
@app.route('/')
def home():
    """
    This function just responds to the browser ULR
    localhost:5000/
    :return:        the rendered template 'home.html'
    """
    return render_template('home.html')

# If we're running in stand alone mode, run the application
if __name__ == '__main__':
    app.run(port=5000)

Swagger.yml

    swagger: "2.0"
info:
  description: This is the swagger file that goes with our server code
  version: "1.0.0"
  title: Swagger REST Article
consumes:
  - "application/json"
produces:
  - "application/json"

basePath: "/api"

# Paths supported by the server application
paths:
  /suggestions:
    get:
      operationId: SqlliteHandler.get_all_names
      tags:
        - suggestions
      summary: The names data structure supported by the server application
      description: Read the list of names
      responses:
        200:
          description: Successful read names list operation
          schema:
            type: array
            items:
              properties:
                name:
                  type: string
                image:
                  type: string
    post:
      operationId: SqlliteHandler.add_name
      tags:
        - suggestions
      summary: Create a name and add it to the names list
      description: Create a new name in the names list
      parameters:
        - name: suggestion
          in: body
          description: Suggestion you want to add to the sprint
          required: True
          schema:
            type: object
            properties:
              name:
                type: string
                description: Name you want to submit
              image:
                type: string
                description: path to the picture of that name
      responses:
        201:
          description: Successfully created name in list

  /suggestions/{suggestion_name}:
    get:
      operationId: SqlliteHandler.get_name
      tags:
        - suggestions
      summary: Read one name from the names list
      description: Read one name from the names list
      parameters:
        - name: suggestion_name
          in: path
          description: name of the sprint name to get from the list
          type: string
          required: True
      responses:
        200:
          description: Successfully read name from names list operation
          schema:
            type: object
            properties:
              name:
                type: string
              image:
                type: string

    put:
      operationId: SqlliteHandler.update_image
      tags:
        - suggestions
      summary: Update an image in the suggestion list via the name of the suggestions
      description: Update an image in the suggestion list
      parameters:
        - name: suggestion_name
          in: path
          description: Suggestion you want to edit
          type: string
          required: True
        - name: suggestion
          in: body
          schema:
            type: object
            properties:
              name:
                type: string
              image:
                type: string
      responses:
        200:
          description: Successfully updated suggestion in suggestion list

    delete:
      operationId: SqlliteHandler.delete_name
      tags:
        - suggestions
      summary: Delete a suggestion via its name from the suggestion list
      description: Delete a suggestion
      parameters:
        - name: suggestion_name
          in: path
          type: string
          required: True
      responses:
        200:
          description: Successfully deleted a suggestion from the list

1 Ответ

0 голосов
/ 08 ноября 2018

Чтобы сохранить изображение в SQLITE (, что не рекомендуется, лучше сохранить изображение в виде файла и сохранить путь в БД ), сохранить его как массив байтов (тип хранения BLOB, столбец не должен быть определен как BLOB).

В SQL вы указываете массив байтов в виде шестнадцатеричной строки. Итак, вы читаете свое изображение и строите шестнадцатеричную строку

  • * 1011 отмечая *

    • Максимальная длина строки или BLOB

      Определено максимальное количество байтов в строке или BLOB в SQLite. макросом препроцессора SQLITE_MAX_LENGTH. Значение по умолчанию этого макрос - 1 миллиард (1 тысяча миллионов или 1 000 000 000). Вы можете увеличить или уменьшить это значение во время компиляции, используя параметр командной строки как это:

      -DSQLITE_MAX_LENGTH = 123456789 Текущая реализация будет поддерживать только строку или длину BLOB до 231-1 или 2147483647. И некоторые встроенные функции, такие как hex (), могут выйти из строя задолго до этого. В чувствительных к безопасности приложений лучше не пытаться увеличить максимальная длина строки и капли. На самом деле, вы могли бы преуспеть, чтобы снизить максимальная длина строки и BLOB-объекта до чего-то большего в диапазоне несколько миллионов, если это возможно.

      Во время обработки SQLite INSERT и SELECT завершается содержимое каждой строки в базе данных кодируется как один большой двоичный объект. Так параметр SQLITE_MAX_LENGTH также определяет максимальное количество байт подряд.

      Максимальная длина строки или BLOB может быть уменьшена во время выполнения с помощью Интерфейс sqlite3_limit (дБ, SQLITE_LIMIT_LENGTH, размер).

Также

  • отмечая
    • Максимальная длина оператора SQL

      Максимальное количество байтов в тексте оператора SQL ограничено в SQLITE_MAX_SQL_LENGTH, который по умолчанию равен 1000000. Вы можете переопределить этот предел должен быть таким же большим, как и меньшее из SQLITE_MAX_LENGTH и 1073741824.

      Если оператор SQL ограничен длиной в миллион байтов, то очевидно, вы не сможете вставить многомиллионные строки байтов встраивая их как литералы внутри операторов INSERT. Но ты должен не делай этого в любом случае. Используйте параметры хоста для ваших данных. Готовь коротко SQL-операторы вроде этого:

      INSERT INTO tab1 VALUES (?,?,?); Затем используйте sqlite3_bind_XXXX () функции для привязки ваших больших строковых значений к оператору SQL. использование связывания устраняет необходимость экранирования кавычек в строка, снижающая риск атак SQL-инъекций. Это также работает быстрее, так как большую строку не нужно анализировать или копировать как много.

      Максимальная длина оператора SQL может быть уменьшена во время выполнения используя интерфейс sqlite3_limit (db, SQLITE_LIMIT_SQL_LENGTH, size).

Результирующий SQL будет выглядеть следующим образом: -

INSERT INTO mytable (myimage) VALUES (x'fffe004577aabbcc33f1f8');

В качестве демонстрации с использованием вашей таблицы (слегка изменено, чтобы включить "правильный" тип столбца BLOB, что не имеет большого значения): -

DROP TABLE If EXISTS sprint_names;
CREATE TABLE if not exists sprint_names ( name text, image text, altimage BLOB);
INSERT INTO sprint_names VALUES
    ('SPRINT001',x'fffe004577aabbcc33f1f8',x'fffe004577aabbcc33f1f8'), -- obviously image would be larger
    ('SPRINT002',x'99008877665544332211f4d6e9c2aaa8b7b4',x'99008877665544332211f4d6e9c2aaa8b7b4')
;
SELECT * FROM sprint_names;

Результат будет: -

enter image description here

  • Примечание. Navicat использовался для запуска текста выше. BLOB-объекты по своей сути трудно отображать, следовательно, отображать. Однако показано, что вышеприведенное явно хранит и извлекает данные.

Как указывалось ранее, гораздо проще просто сохранить путь к файлу изображения, и когда он сводится к нему, вероятно, очень мало потребности в изображении в качестве данных. Вы вряд ли будете запрашивать данные, из которых состоит изображение, в то время как использование стандартов именования может позволить полезные поиски / запросы сохраненного имени / пути.

Однако, вопреки вышесказанному, SQLite может в некоторых случаях (изображения со средним размером около 100 КБ или меньше (возможно, больше)) обеспечить более быстрый доступ, чем файловая система 35% быстрее, чем файловая система .

...