【问题标题】:Insert binary file in SQLite database with Python使用 Python 在 SQLite 数据库中插入二进制文件
【发布时间】:2011-03-19 15:26:26
【问题描述】:

我正在尝试编写一个简单的 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()

我没有收到任何错误消息,但据我所知,没有插入记录。我究竟做错了什么?另外,如何将存储的文档提取回来?谢谢!

【问题讨论】:

    标签: python sql sqlite binary blob


    【解决方案1】:

    不确定您使用的 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'

    注意需要使用buffer 插入blob,并使用str 将其作为字符串读回(因为它也使用buffer 类型作为结果)——如果你只是去将其写入磁盘不需要后一段(因为文件的write 方法确实接受缓冲区对象,就像它接受字符串一样)。

    【讨论】:

    • 完美!非常感谢您的帮助!
    • 很好的例子,我有一个类似的问题 (stackoverflow.com/questions/3915888/…),如果你能看一下,我将不胜感激。
    • 我收到以下错误“sqlite3.InterfaceError: Error binding parameter 7 - 可能是不受支持的类型。”
    • Binary 是 Python 缓冲区的别名。
    • python3 中的缓冲区现在是 memoryview
    【解决方案2】:

    给定的示例存在多个问题。我将会 一一解决。

    • 没有错误检查。我们要么需要使用 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 别名

    【讨论】:

    • 为什么以lite 的身份访问sqlite3
    • 但是你只使用一次。太奇怪了……我看不到胜利。如果我们进行字符计数,它不会缩短代码。
    • 实际上是两次。这更多是个人品味的问题。而不是严格的规定。说在两年内,会有 sqlite4 模块。而且我们不需要更正旧代码中所有出现的模块名称。但是,这些事情在小代码示例中是无关紧要的。
    • 将它作为 sqlite 而不是 lite 将提高可读性,并且仍然是您的方法的“未来证明”。或者只是将其命名为“sql”,因为“lite”实际上并没有任何意义,而“sql”是数据库操作的标准。
    • 我通常将其缩写为“db”,因为名称中带有数字感觉有点不对劲。
    【解决方案3】:

    问题:

    1. 您没有显示您运行的完整代码。您不应该让回答者猜测sqlite.Binary(k) 之类的东西。

    2. 基本问题:您没有提交交易。conn.close()之前使用conn.commit()

    【讨论】:

    • ...k 是来自f.read 的数据,sqlite.Binary 是 sqlite3 模块中的一个函数,对此有何猜测? [注意,我不确定 sqlite.Binary 对 Path.read_bytes() 做了什么,但 OP 正在运行的内容似乎很明显。]
    猜你喜欢
    • 2011-02-12
    • 2012-07-02
    • 1970-01-01
    • 2014-09-29
    • 2010-12-03
    • 1970-01-01
    • 1970-01-01
    • 2014-04-11
    • 2021-08-02
    相关资源
    最近更新 更多