使用Python Flask/Connexion和Swagger在API中处理图像

使用Python Flask/Connexion和Swagger在API中处理图像,python,image,sqlite,flask,swagger-ui,Python,Image,Sqlite,Flask,Swagger Ui,我试图设置一个非常简单的应用程序。我想将此应用程序创建为fullstack应用程序,作为未来项目的培训。所以我用python编写了一个后端,它通过API(Flask/Connexion)从DB(SQLLite)提供数据。API是通过Swagger记录的。DB应该有一个表,其中每行有2个值: 1.名称 2.图像 我很快就遇到了一个问题:我实际上不知道如何在API中处理图像。因此,我使用占位符创建了备份。到目前为止,图像只是另一个字符串,大部分是空的。一切正常。但现在我希望能够通过API获取图像并将

我试图设置一个非常简单的应用程序。我想将此应用程序创建为fullstack应用程序,作为未来项目的培训。所以我用python编写了一个后端,它通过API(Flask/Connexion)从DB(SQLLite)提供数据。API是通过Swagger记录的。DB应该有一个表,其中每行有2个值: 1.名称 2.图像 我很快就遇到了一个问题:我实际上不知道如何在API中处理图像。因此,我使用占位符创建了备份。到目前为止,图像只是另一个字符串,大部分是空的。一切正常。但现在我希望能够通过API获取图像并将其保存在DB中。我完全不知道怎么做。希望你们中的一个能帮助我

以下是我目前的代码:

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)
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)
RunBackEnd.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)
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)
招摇过市.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

要在SQLITE中保存图像(不建议这样做,最好将图像保存为文件并在DB中保存路径),请将其保存为字节数组(存储类型为BLOB,而不是将列定义为BLOB)

在SQL中,将字节数组指定为十六进制字符串。所以你读了你的图像,并建立了一个十六进制字符串

  • 注意到

    • 字符串或BLOB的最大长度

      定义了SQLite中字符串或BLOB的最大字节数 由预处理器宏SQLITE_MAX_LENGTH执行。此参数的默认值 宏是10亿(10亿或100000000)。你可以 在编译时使用命令行选项提高或降低此值 像这样:

      -DSQLITE_MAX_LENGTH=123456789当前实现将只支持最长为231-1或2147483647的字符串或BLOB长度。还有一些 诸如hex()之类的内置函数可能在这一点之前就失败了。在里面 对安全敏感的应用程序最好不要试图增加 最大字符串和blob长度。事实上,你最好降低价格 字符串和blob的最大长度在 如果可能的话,也只有几百万

      在SQLite的部分插入和选择处理过程中,完成 数据库中每一行的内容都编码为单个BLOB。所以 SQLITE_MAX_LENGTH参数还确定 一行中的字节数

      可以在运行时使用 sqlite3限制(db、SQLITE限制长度、大小)接口

  • 注意到
    • SQL语句的最大长度

      SQL语句文本中的最大字节数是有限的 到SQLITE_MAX_SQL_LENGTH,默认为1000000。你可以重新定义 此限制应与SQLITE_MAX_长度和 1073741824

      如果SQL语句的长度限制为一百万字节,则 显然,您将无法通过插入数百万字节的字符串 将它们作为文本嵌入INSERT语句中。但是你应该 无论如何,不要那样做。对数据使用主机参数。准备简短的 SQL语句如下:

      在表1中插入值(?,?);然后使用sqlite3\u bind\u XXXX() 函数将大字符串值绑定到SQL语句。这个 使用绑定可以避免在 字符串,降低SQL注入攻击的风险。它也在运行 速度更快,因为大字符串不需要作为 很多

      SQL语句的最大长度可以在运行时降低 使用sqlite3_limit(db、SQLITE_limit_SQL_LENGTH、size)接口

生成的SQL大致如下所示:-

INSERT INTO mytable (myimage) VALUES (x'fffe004577aabbcc33f1f8');
作为使用表的演示(稍作修改以包含“正确”的列类型BLOB,这没有什么区别):-

结果将是:-

  • 注:Navicat用于运行上述文本。BLOB本质上很难显示,因此无法显示。然而,所显示的是,上面显然存储和检索了数据
如前所述,只存储图像文件的路径要简单得多,当它归结为图像文件时,可能很少需要图像作为数据。您不太可能查询图像所包含的数据,而使用命名标准可以对存储的名称/路径进行有用的搜索/查询


但是,与上述情况相反,SQLite在某些情况下(平均大小约为100k或更小(可能更大))允许比文件系统更快的访问。

好的,谢谢。在DB中保存路径是我的第一个ID,这就是为什么图像的类型是string:D,所以我想我必须在RunBackEnd.py中定义一个上传文件夹,所有上传的图像都存储在这个文件夹中。但是我怎样才能通过API上传它们呢?@itskajo很抱歉,在这方面我帮不了忙,我从来没有使用过Pyhton,更不用说Flask或Swagger ui了。我猜会有一些教程,也许会有帮助。你可以用很多不同的方式处理图像-你想怎么做?应用程序/八位字节流?base64?甚至序列化的numpy数组。。?你是如何到达终点的,你有一个样本图像吗?您可以将它们作为base64字符串安全地保存在数据库中