【问题标题】:using pyodbc on ubuntu to insert a image field on SQL Server在 ubuntu 上使用 pyodbc 在 SQL Server 上插入图像字段
【发布时间】:2010-11-06 19:09:46
【问题描述】:

我正在使用 Ubuntu 9.04

我安装了以下软件包版本:

unixodbc and unixodbc-dev: 2.2.11-16build3
tdsodbc: 0.82-4
libsybdb5: 0.82-4
freetds-common and freetds-dev: 0.82-4
python2.6-dev

我已经这样配置/etc/unixodbc.ini

[FreeTDS]
Description             = TDS driver (Sybase/MS SQL)
Driver          = /usr/lib/odbc/libtdsodbc.so
Setup           = /usr/lib/odbc/libtdsS.so
CPTimeout               = 
CPReuse         = 
UsageCount              = 2

我已经像这样配置/etc/freetds/freetds.conf

[global]
    tds version = 8.0
    client charset = UTF-8
    text size = 4294967295

我已经从http://github.com/mkleehammer/pyodbc 抓取了pyodbc 修订版31e2fae4adbf1b2af1726e5668a3414cf46b454f 并使用“python setup.py install”安装它

我的本​​地网络上安装了 Microsoft SQL Server 2000 的 Windows 机器,启动并监听本地 IP 地址 10.32.42.69。我有一个名为“Common”的空数据库。我有用户“sa”,密码为“secret”,拥有完全权限。

我正在使用以下 python 代码来设置连接:

import pyodbc
odbcstring = "SERVER=10.32.42.69;UID=sa;PWD=secret;DATABASE=Common;DRIVER=FreeTDS"
con = pyodbc.connect(odbcstring)
cur = con.cursor()

cur.execute("""
IF EXISTS(SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES
      WHERE TABLE_NAME = 'testing')
   DROP TABLE testing
""")
cur.execute('''
CREATE TABLE testing (
    id INTEGER NOT NULL IDENTITY(1,1), 
    myimage IMAGE NULL, 
    PRIMARY KEY (id)
)
    ''')
con.commit()

到目前为止,一切都有效。我在服务器上使用了 SQLServer 的企业管理器,新表就在那里。 现在我想在表格中插入一些数据。

cur = con.cursor()
# using web data for exact reproduction of the error by all.
# I'm actually reading a local file in my real code.
url = 'http://www.forestwander.com/wp-content/original/2009_02/west-virginia-mountains.jpg'
data = urllib2.urlopen(url).read()

sql = "INSERT INTO testing (myimage) VALUES (?)"

现在在我原来的问题上,我在使用 cur.execute(sql, (data,)) 时遇到了问题,但现在我已经编辑了这个问题,因为按照下面 Vinay Sajip 的回答(谢谢),我已将其更改为:

cur.execute(sql, (pyodbc.Binary(data),)) 
con.commit()

插入运行良好。我可以使用以下测试代码确认插入数据的大小:

cur.execute('SELECT DATALENGTH(myimage) FROM testing WHERE id = 1')
data_inside = cur.fetchone()[0]
assert data_inside == len(data)

通过完美!!!

现在问题在于检索数据。

我正在尝试通用方法:

cur.execute('SELECT myimage FROM testing WHERE id = 1')
result = cur.fetchone()
returned_data = str(result[0]) # transforming buffer object
print 'Original: %d; Returned: %d' % (len(data), len(returned_data))
assert data == returned_data

但是失败了!!

Original: 4744611; Returned: 4096
Traceback (most recent call last):
  File "/home/nosklo/devel/teste_mssql_pyodbc_unicode.py", line 53, in <module>
    assert data == returned_data
AssertionError

我已将以上所有代码放在一个文件here 中,以便于测试任何想要提供帮助的人。

现在问题来了:

我希望 python 代码将图像文件插入 mssql。我想查询图像并将其显示给用户。

我不关心 mssql 中的列类型。我在示例中使用“IMAGE”列类型,但任何二进制/blob 类型都可以,只要我得到未损坏的插入文件的二进制数据。 Vinay Sajip 在下面说,这是 SQL SERVER 2000 中的首选数据类型。

现在插入数据没有错误,但是当我检索数据时,只返回 4k。 (数据在 4096 被截断)。

我怎样才能做到这一点?


编辑:下面 Vinay Sajip 的回答给了我在现场使用 pyodbc.Binary 的提示。我已经相应地更新了这个问题。谢谢 Vinay Sajip!

Alex Martelli 的评论让我想到了使用DATALENGTH MS SQL 函数来测试数据是否已完全加载到列上。谢谢亚历克斯·马泰利!

【问题讨论】:

  • 当您“从测试中选择 DATALENGTH(myimage)”时会得到什么?至少这会告诉你问题是存储还是检索。

标签: python sql-server image pyodbc freetds


【解决方案1】:

嗯,刚出完悬赏,我就找到了解决办法。

除了/etc/freetds/freetds.conf 中的文本大小配置选项外,您还必须在查询中使用SET TEXTSIZE 2147483647

我用过

cur.execute('SET TEXTSIZE 2147483647 SELECT myimage FROM testing WHERE id = 1')

一切正常。

奇怪的是FreeTDS documentation says关于文本大小配置选项:

TEXTSIZE 的默认值,以字节为单位。对于textimage 数据类型,设置任何返回列的最大宽度。参照。 set TEXTSIZE 在您的服务器的 T-SQL 文档中。

配置还说最大值(和默认值)是 4,294,967,295。但是,当尝试在查询中使用该值时出现错误,我可以在查询中使用的最大数字是 2,147,483,647(一半)。

根据该解释,我认为仅设置此配置选项就足够了。事实证明我错了,在查询中设置 TEXTSIZE 解决了这个问题。

下面是完整的工作代码:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import pyodbc
import urllib2

odbcstring = "SERVER=10.32.42.69;UID=sa;PWD=secret;DATABASE=Common;DRIVER=FreeTDS"
con = pyodbc.connect(odbcstring)
cur = con.cursor()

cur.execute("""
IF EXISTS(SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES
      WHERE TABLE_NAME = 'testing')
   DROP TABLE testing
""")

cur.execute('''
CREATE TABLE testing (
    id INTEGER NOT NULL IDENTITY(1,1), 
    myimage IMAGE NULL,
    PRIMARY KEY (id)
)
    ''')

con.commit()
cur = con.cursor()
url = 'http://www.forestwander.com/wp-content/original/2009_02/west-virginia-mountains.jpg'
data = urllib2.urlopen(url).read()

sql = "INSERT INTO testing (myimage) VALUES (?)"
cur.execute(sql, (pyodbc.Binary(data),))
con.commit()

cur.execute('SELECT DATALENGTH(myimage) FROM testing WHERE id = 1')
data_inside = cur.fetchone()[0]
assert data_inside == len(data)

cur.execute('SET TEXTSIZE 2147483647 SELECT myimage FROM testing WHERE id = 1')
result = cur.fetchone()
returned_data = str(result[0])
print 'Original: %d; Returned; %d' % (len(data), len(returned_data))
assert data == returned_data

【讨论】:

    【解决方案2】:

    我认为您应该使用pyodbc.Binary 实例来包装数据:

    cur.execute('INSERT INTO testing (myimage) VALUES (?)', (pyodbc.Binary(data),))
    

    检索应该是

    cur.execute('SELECT myimage FROM testing')
    print "image bytes: %r" % str(cur.fetchall()[0][0])
    

    更新:问题在于插入。将插入 SQL 更改为以下内容:

    """DECLARE @txtptr varbinary(16)
    
    INSERT INTO testing (myimage) VALUES ('')
    SELECT @txtptr = TEXTPTR(myimage) FROM testing 
    WRITETEXT testing.myimage @txtptr ?
    """
    

    我还更新了我在检索代码中使用 value 属性时犯的错误。

    通过此更改,我可以在数据库中插入和检索 320K JPEG 图像(检索到的数据与插入的数据相同)。

    注意image 数据类型已被弃用,并在 SQL Server 的更高版本中被 varbinary(max) 替换。但是,对于较新的列类型,应该应用相同的插入/检索逻辑。

    【讨论】:

    • 正确,尽管我必须使用 str(cur.fetchall()[0][0]) 来获取数据。 .value 返回:AttributeError:“缓冲区”对象没有属性“值”
    • 我仍然有问题。我已经更新了问题,请看一下。
    • 感谢 Vinay 的更新。原来问题不在于插入。我已经尝试了 TEXTPTR 代码并得到了完全相同的结果。谢谢你的时间。我再次编辑了问题以显示当前问题。你能给我你说有效的完整代码吗?你能给我关于你的设置的详细信息吗?我仍然无法让它工作。如果要求不高,您能否在您的设置上运行我的代码并告诉我结果?我已经在paste.pocoo.org/show/125955 上提供了它,它使用互联网上的一个文件,所以它的运行方式完全相同。
    • 目前我没有从 Jaunty 访问 SQL Server 2000;并且您的代码(发布在 LodgeIt 上)在 Windows 上非常适合我。所以它可能与 FreeTDS 驱动程序或 Linux 堆栈中的其他东西有关。抱歉,我无法提供更多帮助。
    【解决方案3】:

    我在TEXT 字段上遇到了类似的4096 截断问题,SET TEXTSIZE 2147483647 为我解决了这个问题,但这也为我解决了问题:

    import os
    os.environ['TDSVER'] = '8.0'
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-10-31
      • 1970-01-01
      • 2018-11-13
      • 1970-01-01
      • 2017-07-02
      • 1970-01-01
      相关资源
      最近更新 更多