ubuntuでpyodbcを使用してSQL Serverに画像フィールドを挿入する
-
21-08-2019 - |
質問
使っています 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
pyodbcリビジョンを取得しました 31e2fae4adbf1b2af1726e5668a3414cf46b454f
から http://github.com/mkleehammer/pyodbc
「」を使用してインストールしましたpython setup.py install
"
私はWindowsマシンを持っています Microsoft SQL Server 2000 私のローカルネットワークにインストールされており、ローカル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 の Enterprise Manager を使用しており、新しいテーブルがそこにあります。次に、テーブルにデータを挿入したいと思います。
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
上記のコードをすべて 1 つのファイルにまとめました ここ, 、助けたい人を簡単にテストできます。
さて、質問です。
Python コードで画像ファイルを mssql に挿入したいと考えています。画像をクエリしてユーザーに表示したいと考えています。
mssql の列の型は気にしません。私が使っているのは「」IMAGE
この例では列タイプを使用していますが、挿入して戻したファイルのバイナリ データをそのままの状態で取得できる限り、任意のバイナリ/ブロブ タイプを使用できます。Vinay Sajip 氏は以下で、これが SQL SERVER 2000 で推奨されるデータ型であると述べています。
データはエラーなく挿入されていますが、データを取得すると 4k のみが返されます。(データは 4096 で切り捨てられます)。
どうすればそれを機能させることができますか?
編集:以下の Vinay Sajip の回答は、フィールドで pyodbc.Binary を使用するためのヒントを与えてくれました。それに応じて質問を更新しました。ありがとうビネイ・サジップ!
Alex Martelli のコメントから、 DATALENGTH
データが列に完全にロードされているかどうかをテストする MS SQL 関数。アレックス・マルテッリ、ありがとう!
解決
えー、賞金を出した直後に解決策が分かりました。
使用する必要があります SET TEXTSIZE 2147483647
クエリのテキスト サイズ設定オプションに加えて、 /etc/freetds/freetds.conf
.
利用した
cur.execute('SET TEXTSIZE 2147483647 SELECT myimage FROM testing WHERE id = 1')
そしてすべてがうまくいきました。
奇妙なことは何ですか FreeTDS のドキュメントには次のように書かれています テキストサイズ設定オプションについて:
のデフォルト値
TEXTSIZE
, 、バイト単位。のためにtext
そしてimage
データ型は、返される列の最大幅を設定します。参照。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
他のヒント
を使用する必要があると思います 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
データ型は非推奨になり、次のものに置き換えられます。 varbinary(max)
SQL Server の以降のバージョンでは。ただし、新しい列タイプにも同じ挿入/取得ロジックが適用されます。
私も同様のものを持っていました 4096
切り捨ての問題 TEXT
フィールド、 SET TEXTSIZE 2147483647
私にとっては修正されましたが、これも私にとっては修正されました:
import os
os.environ['TDSVER'] = '8.0'