在不使用准备好的查询的情况下将 QPixmap 保存到 PostgreSQL

Saving QPixmap to PostgreSQL without using prepared queries

有很多关于如何使用绑定方法将 QPixmap(或 QImage)保存到 Sqlite 的答案,但是如果我的数据库不支持绑定方法(如 PostgreSQL)怎么办?有没有办法在原始 SQL 中做到这一点?

我将示例从 Qt Wiki example 转换为 PyQt 以更好地解释我的问题:

import sys

from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtSql import *

app = QApplication([])

db = QSqlDatabase.addDatabase( "QSQLITE" )
db.setDatabaseName( ":memory:" )
db.open()

query = QSqlQuery(db)
query.exec("CREATE TABLE IF NOT EXISTS imgTable (filename TEXT, imagedata BLOB)")

screen = app.primaryScreen()
inPixmap = screen.grabWindow(0)
byteArray = QByteArray()
buffer = QBuffer(byteArray)
buffer.open(QIODevice.WriteOnly)
inPixmap.save(buffer, "PNG")

# Insert image bytes into the database
query.prepare("INSERT INTO imgTable (filename, imagedata) VALUES ('screenshot.png', :imageData)")
query.bindValue(":imageData", byteArray)
query.exec()

# Get image data back from database
query.exec("SELECT imagedata from imgTable")
query.first()
#outByteArray = query.value(0).toByteArray() # doesn't work
outByteArray = query.value(0)
outPixmap = QPixmap()
outPixmap.loadFromData(outByteArray)

db.close()

# Display image
myLabel = QLabel()
myLabel.setPixmap( outPixmap )
myLabel.show()

sys.exit(app.exec())

这个例子适用于我的 Win 机器。它抓取屏幕截图,将其存储到 Sqlite 数据库,从数据库中检索并将其显示为 QLabel。

我的问题: 当您可以使用时,将图像存储到 Sqlite 数据库很容易 query.bindValue(":imageData", byteArray)

但是我的 PostgreSQL 数据库不支持准备好的查询。例如,对于 PostgreSQL db.driver().hasFeature(QSqlDriver.PreparedQueries) returns False.

所以,我不能使用:

query.prepare("INSERT INTO imgTable (filename, imagedata) VALUES ('screenshot.png', :imageData)")
query.bindValue(":imageData", byteArray)
query.exec()

我只能用raw SQL:

query.exec("INSERT INTO imgTable (filename, imagedata) VALUES ('screenshot.png', i_dont_know_how_to_store_byteArray_here)")

为了将值存储到 PostgreSQL 数据库,此查询应该是什么样子,在这种情况下,字段 imagedata 是 bytea 并且 table 是这样创建的:

query.exec("CREATE TABLE IF NOT EXISTS imgTable (filename TEXT, imagedata BYTEA)")

在这种情况下,解决方案是在 base64 中对字节进行编码并连接字符串(显然您必须验证查询,因为不使用占位符可能容易受到 SQL 注入攻击):

import os
import sys

from PyQt5 import QtCore, QtSql, QtGui, QtWidgets


def create_connections():
    db = QtSql.QSqlDatabase.addDatabase("QPSQL")
    db.setHostName("localhost")
    db.setDatabaseName("db")
    db.setUserName("user")
    db.setPassword("pass")
    if not db.open():
        print(db.lastError().text())
        return False
    query_str = (
        """CREATE TABLE IF NOT EXISTS imgTable (filename TEXT, imagedata BYTEA)"""
    )
    query = QtSql.QSqlQuery(query_str)
    if not query.exec_():
        print(query.lastError().text())
    return True


if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    if not create_connections():
        sys.exit(-1)

    screen = QtWidgets.QApplication.primaryScreen()
    inPixmap = screen.grabWindow(0)
    byteArray = QtCore.QByteArray()
    qbuffer = QtCore.QBuffer(byteArray)
    qbuffer.open(QtCore.QIODevice.WriteOnly)
    inPixmap.save(qbuffer, "PNG")
    data = byteArray.toBase64().data().decode()

    query = QtSql.QSqlQuery()
    RAW_SQL = """INSERT INTO imgTable (filename, imagedata) VALUES ('screenshot.png', '{}')""".format(
        data
    )
    if not query.exec_(RAW_SQL):
        print(query.lastError().text())
        sys.exit(-1)

    q = QtSql.QSqlQuery("SELECT filename, imagedata FROM imgTable")
    if not q.first():
        print(q.lastError().text())
        sys.exit(-1)

    filename = q.value(0)
    data = q.value(1)

    ba = QtCore.QByteArray.fromBase64(data)
    pixmap = QtGui.QPixmap()
    pixmap.loadFromData(ba)
    w = QtWidgets.QWidget()
    label_pixmap = QtWidgets.QLabel()
    label_text = QtWidgets.QLabel()
    lay = QtWidgets.QVBoxLayout(w)
    lay.addWidget(label_pixmap)
    lay.addWidget(label_text)
    label_pixmap.setPixmap(pixmap)
    label_text.setText(filename)
    w.show()
    app.exec_()