在不使用准备好的查询的情况下将 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_()
有很多关于如何使用绑定方法将 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_()