用Python将二进制文件插入SQLite数据库

18 投票
3 回答
41079 浏览
提问于 2025-04-16 01:41

我正在尝试写一个简单的Python脚本,把.odt文档插入到SQLite数据库里。到目前为止,我做了这些,但似乎没有成功:

f=open('Loremipsum.odt', 'rb')
k=f.read()
f.close()
cursor.execute="INSERT INTO notes (note) VALUES ('%s')" %(sqlite.Binary(k))
cursor.close()
conn.close()

我没有收到任何错误信息,但从我看到的情况来看,记录并没有被插入。我哪里做错了?另外,我该如何把存储的文档提取出来呢?谢谢!

3 个回答

9

问题:

  1. 你没有展示你运行的完整代码。不要让回答的人去猜像 sqlite.Binary(k) 这样的东西是什么。

  2. 根本问题:你没有提交你的事务。conn.close() 之前要使用 conn.commit()

13

这个例子有几个问题,我会一个一个来讲。

  • 没有错误检查。我们需要使用try/except/finally结构,或者用with关键字。
  • Python的方法和C#的属性不一样。你不是在运行execute方法,而是在给一个对象赋值一个字符串。(在Python中,方法也是对象。)
  • 非常重要的一点是,你的代码容易受到SQL注入攻击。我们绝对不应该用Python的字符串操作来构建SQL语句。我们应该始终使用占位符
  • 这个例子不完整,这会导致一个棘手的问题。假设有一个CREATE TABLE语句,那么会创建一个新的隐式事务。必须发出commit语句才能把数据保存到数据库文件中。在SQLite中,除了SELECT以外的任何语句都会开始一个隐式事务。(有些数据库,比如MySQL,默认是自动提交模式,但SQLite不是。)

下面是一个正确的示例,它将一个LibreOffice文档写入SQLite数据库的docs表:

#!/usr/bin/env python

import sqlite3


def readData():

    fl = open('book.odt', 'rb')

    with fl:
        data = fl.read()

    return data


con = sqlite3.connect('test.db')

with con:

    cur = con.cursor()     
    cur.execute("CREATE TABLE IF NOT EXISTS docs(Data BLOB)")

    data = readData()

    sql = "INSERT INTO docs(Data) VALUES (?)" 
    cur.execute(sql, (sqlite3.Binary(data), ))

book.odt文件位于当前工作目录中。我们没有手动调用commit方法,因为这由with关键字在后台处理。

编辑去掉了lite别名

37

我不太确定你用的 sqlite.Binary 是什么,不过没关系,这里有个可以运行的例子:

import sqlite3

# let's just make an arbitrary binary file...
with open('/tmp/abin', 'wb') as f:
  f.write(''.join(chr(i) for i in range(55)))
# ...and read it back into a blob
with open('/tmp/abin', 'rb') as f:
  ablob = f.read()

# OK, now for the DB part: we make it...:
db = sqlite3.connect('/tmp/thedb')
db.execute('CREATE TABLE t (thebin BLOB)')
db.execute('INSERT INTO t VALUES(?)', [buffer(ablob)])
db.commit()
db.close()

# ...and read it back:
db = sqlite3.connect('/tmp/thedb')
row = db.execute('SELECT * FROM t').fetchone()
print repr(str(row[0]))

在 Python 2.6 下运行这段代码时,结果会显示出你预期的内容:

'\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !"#$%&\'()*+,-./0123456'

注意,在插入二进制大对象(blob)时需要用到 buffer,而读取时则用 str 来把它转回字符串(因为读取的结果也是 buffer 类型)——如果你只是想把它写到磁盘上,后面的这一步就不需要了(因为文件的 write 方法既可以接受 buffer 对象,也可以接受字符串)。

撰写回答