2009-06-29 14 views
7

Używam Ubuntu 9.04użyciu pyodbc na ubuntu aby wstawić pole obrazu na SQL Server

Mam zainstalowaną następujące wersje pakietu:

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 

mam skonfigurowane /etc/unixodbc.ini tak:

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

Mam skonfigurowane /etc/freetds/freetds.conf w ten sposób:

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

ja porwałem zmianę pyodbc 31e2fae4adbf1b2af1726e5668a3414cf46b454f z http://github.com/mkleehammer/pyodbc i zainstalować go za pomocą "python setup.py install"

Mam maszynę Windows z Microsoft SQL Server 2000 zainstalowany w sieci lokalnej, i słuchać na lokalnym ip Adres 10.32.42.69. Mam pustą bazę danych utworzoną z nazwą "Common". Mam użytkownika "sa" z hasłem "secret" z pełnymi uprawnieniami.

Używam następujący kod Pythona do konfiguracji połączenia:

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() 

Wszystko WORKS do tego punktu. Użyłem Enterprise Manager SQLServer na serwerze i tam jest nowa tabela. Teraz chcę wstawić niektóre dane do tabeli.

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 (?)" 

Teraz tutaj na moje pierwotne pytanie, miałem kłopoty z wykorzystaniem cur.execute(sql, (data,)) ale teraz mam edytowane pytanie, ponieważ po odpowiedź VINAY Sajip jest poniżej (dzięki), zmieniłem go na adres:

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

I wstawianie działa idealnie. Mogę potwierdzić rozmiar danych wstawianych przy użyciu następującego kodu testu:

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

który przechodzi doskonale !!!

Teraz problem polega na odzyskaniu danych.

Staram wspólne podejście:

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 

jednak, że zawiedzie !!

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 

Wrzuciłem cały powyższy kod w jednym pliku here, dla łatwego sprawdzenia każdego, kto chce pomóc.

Teraz na pytanie:

Chcę kod Pythona, aby wstawić plik obrazu do mssql. Chcę przesłać zapytanie o obraz i pokazać go użytkownikowi.

Nie obchodzi mnie typ kolumny w mssql.Używam typu kolumny "IMAGE" na przykładzie, ale zrobi to dowolny typ binarny/blobowy, o ile otrzymam dane binarne dla pliku, który wstawiłem z powrotem w niezałatwiony. Vinay Sajip powiedział poniżej, że jest to preferowany typ danych dla tego w SQL SERVER 2000.

Dane są teraz wstawiane bez błędów, jednak po pobraniu danych zwracane są tylko 4k. (Dane są obcinane na 4096).

Jak mogę wykonać tę pracę?


edytuje: poniżej odpowiedź VINAY Sajip dał mi wskazówkę do korzystania pyodbc.Binary na boisku. Odpowiednio zaktualizowałem pytanie. Dzięki Vinay Sajip!

Komentarz Alexa Martellego dał mi pomysł użycia funkcji MS SQL DATALENGTH do sprawdzenia, czy dane są w pełni załadowane w kolumnie. Dzięki Alex Martelli!

+0

co czy otrzymujesz, gdy "WYBIERZ DATALENGTH (myimage) z testowania"? Przynajmniej to powie ci, czy masz problem ze składowaniem, czy z pobieraniem. –

Odpowiedz

5

Huh, zaraz po zaoferowaniu nagrody, znalazłem rozwiązanie.

Musisz użyć SET TEXTSIZE 2147483647 w zapytaniu, oprócz opcji konfiguracji rozmiaru tekstu w /etc/freetds/freetds.conf.

Użyłem

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

i wszystko działało w porządku.

FreeTDS documentation says Dziwne jest to, co na temat opcji konfiguracji rozmiar tekstu:

default value of TEXTSIZE , in bytes. For text and image datatypes, sets the maximum width of any returned column. Cf. set TEXTSIZE in the T-SQL documentation for your server.

Konfiguracja mówi również, że maksymalna wartość (i domyślne) 4294967295. Jednak przy próbie użycia tej wartości w zapytaniu pojawia się błąd, maksymalna liczba, której mogę użyć w zapytaniu to 2 147 483 647 (połowa).

Z tego wyjaśnienia myślałem, że wystarczy ustawić tę opcję konfiguracji. Okazuje się, że się myliłem, ustawienie TEXTSIZE w zapytaniu rozwiązało problem.

Poniżej pełna kod roboczych:

#!/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 
3

myślę należy używać instancji pyodbc.Binary zawinąć dane:

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

Pobieranie powinno być

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

UPDATE: Problem jest w założeniu. Zmień kod wstawiania SQL na:

"""DECLARE @txtptr varbinary(16) 

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

Zaktualizowałem również błąd, który popełniłem, używając atrybutu value w kodzie pobierania.

Dzięki tej zmianie mogę wstawiać i pobierać obraz JPEG o rozdzielczości 320 KB do bazy danych (pobrane dane są identyczne z wprowadzonymi danymi).

N.B. Typ danych image jest przestarzały i jest zastępowany przez varbinary(max) w późniejszych wersjach SQL Server. Ta sama logika wstawiania/pobierania powinna jednak dotyczyć nowszego typu kolumny.

+0

Prawidłowo, chociaż musiałem użyć str (cur.fetchall() [0] [0]), aby uzyskać dane. .value zwrócony: AttributeError: obiekt "bufor" nie ma atrybutu "wartość" – nosklo

+0

Nadal występują problemy. Zaktualizowałem pytanie, proszę spojrzeć. – nosklo

+0

Than Vinay dla aktualizacji. Okazuje się, że problem nie jest w trakcie wstawiania. Próbowałem kodu TEXTPTR i otrzymałem dokładnie ten sam wynik. Dziękuję za Twój czas. Ponownie zredagowałem pytanie, aby pokazać bieżący numer. Czy możesz dać mi pełny kod, który powiedziałeś, że zadziałał? Czy możesz podać mi szczegóły dotyczące konfiguracji? Nadal nie mogę sprawić, żeby działało. Jeśli nie pytasz o wiele, czy możesz również uruchomić mój kod niezmieniony w konfiguracji i podać mi wyniki? Udostępniłem go na http://paste.pocoo.org/show/125955/ i używa pliku w Internecie, aby działał dokładnie tak samo. – nosklo

1

miałem podobny problem 4096 obcinania na TEXT pól, które SET TEXTSIZE 2147483647 ustalonych dla mnie, ale także stała się dla mnie:

import os 
os.environ['TDSVER'] = '8.0' 
Powiązane problemy