【问题标题】:Handling Images in API with Python Flask/Connexion and Swagger使用 Python Flask/Connexion 和 Swagger 在 API 中处理图像
【发布时间】:2018-11-07 13:44:50
【问题描述】:

我尝试设置一个非常简单的应用程序。我想将这个应用程序创建为全栈应用程序,作为未来项目的培训。所以我用python编写了一个后端,它通过API(Flask/Connexion)从数据库(SQLLite)提供数据。 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

【问题讨论】:

  • 你可以用很多不同的方式处理图片——你会喜欢吗?应用程序/八位字节流?基数64?甚至序列化的numpy数组..?你是如何到达终点的,你有样本图像吗?您可以将它们作为 base64 字符串安全地保存在数据库中

标签: python image sqlite flask swagger-ui


【解决方案1】:

要在 SQLITE 中保存图像(不推荐,最好将图像保存为文件并将路径保存在数据库中),请将其保存为一个字节数组(存储类型为 BLOB,而不是必须将列定义为 BLOB)。

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

  • 注意

    • 字符串或BLOB的最大长度

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

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

      在 SQLite 的部分 INSERT 和 SELECT 处理过程中,完整的 数据库中每一行的内容被编码为单个 BLOB。所以 SQLITE_MAX_LENGTH 参数还决定了最大数量 连续字节。

      最大字符串或 BLOB 长度可以在运行时使用 sqlite3_limit(db,SQLITE_LIMIT_LENGTH,size) 接口。

还有

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

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

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

      插入 tab1 值(?,?,?);然后使用 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;

结果是:-

  • 注意 Navicat 用于运行上述文本。 Blob 本身就很难显示,因此无法显示。但是,显示的是上面显然存储和检索数据。

如前所述,仅存储图像文件的路径要简单得多,归结为它可能很少需要图像作为数据。您不太可能查询图像所包含的数据,而使用命名标准可以允许对存储的名称/路径进行有用的搜索/查询。

然而,与上述相反,SQLite 在某些情况下(平均大小约为 100k 或更小(可能更多)的图像)允许比文件系统35% Faster Than The Filesystem 更快的访问。

【讨论】:

  • 好的,谢谢。在数据库中保存路径是我的第一个 ID,这就是为什么图像是字符串类型的原因:D 所以我想我必须在我的 RunBackEnd.py 中定义一个上传文件夹,所有上传的图像都存储在其中。但是如何通过 API 上传它们?
  • @itskajo 抱歉,我在这方面无能为力,我从未使用过 Pyhton,更不用说 Flask 或 Swagger-ui。我猜周围会有教程,也许Upload a file in Swagger and receive at Flask backend 可以提供帮助。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-07-16
  • 2020-09-11
  • 2014-06-24
  • 1970-01-01
  • 2021-12-18
  • 2015-07-26
相关资源
最近更新 更多