2009-06-29 11 views
7

Sto usando Ubuntu 9.04utilizzando pyodbc su Ubuntu per inserire un campo di immagine su SQL Server

Ho installato le seguenti versioni del pacchetto:

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 

Ho configurato /etc/unixodbc.ini come questo:

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

ho configurato /etc/freetds/freetds.conf come questo:

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

ho afferrato revisione pyodbc 31e2fae4adbf1b2af1726e5668a3414cf46b454f da http://github.com/mkleehammer/pyodbc e installato tramite "python setup.py install"

Ho una macchina Windows con Microsoft SQL Server 2000 installato sulla mia rete locale, e ascoltando sul IP locale indirizzo 10.32.42.69. Ho un database vuoto creato con il nome "Comune". Ho l'utente "sa" con password "secret" con tutti i privilegi.

Sto usando il seguente codice Python per impostare la connessione:

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

Tutto WORKS fino a questo punto. Ho usato Enterprise Manager di SQLServer sul server e la nuova tabella è lì. Ora voglio inserire alcuni dati sul tavolo.

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

Ora qui sulla mia domanda iniziale, ho avuto problemi con l'uso cur.execute(sql, (data,)) ma ora ho curato la questione, perché in seguito la risposta di Vinay Sajip sotto (Grazie), io che ho cambiato a:

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

E l'inserimento funziona perfettamente. Posso confermare la dimensione dei dati inseriti utilizzando il seguente codice di prova:

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

Che passa perfettamente !!!

Ora il problema riguarda il recupero dei dati.

Sto cercando l'approccio comune:

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 

Tuttavia che non riesce !!

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 

ho messo tutto il codice di cui sopra in un unico file here, per una facile verifica di tutti coloro che vogliono aiutare.

Ora per la domanda:

Voglio codice Python per inserire un file di immagine in mssql. Voglio interrogare l'immagine e mostrarla all'utente.

Non mi interessa il tipo di colonna in mssql.Sto usando il tipo di colonna "IMAGE" nell'esempio, ma qualsiasi tipo di binario/blob farebbe, a patto che ottenga i dati binari per il file che ho inserito di nuovo non rinchiuso. Vinay Sajip ha dichiarato che questo è il tipo di dati preferito per questo in SQL SERVER 2000.

I dati vengono ora inseriti senza errori, tuttavia quando recupero i dati, vengono restituiti solo 4k. (I dati sono troncati su 4096).

Come posso farlo funzionare?


modifiche: la risposta di Vinay Sajip seguito mi ha dato un suggerimento per utilizzare pyodbc.Binary sul campo. Ho aggiornato la domanda di conseguenza. Grazie Vinay Sajip! Il commento di

Alex Martelli mi ha dato l'idea di utilizzare la funzione di DATALENGTH MS SQL per verificare se i dati è a pieno carico sulla colonna. Grazie Alex Martelli!

+0

cosa ottieni quando SELEZIONA DATALENGTH (myimage) DA testare? Almeno questo ti dirà se il tuo problema è con la memorizzazione o il recupero. –

risposta

5

Huh, subito dopo aver offerto la taglia, ho trovato la soluzione.

È necessario utilizzare SET TEXTSIZE 2147483647 sulla query, oltre all'opzione di configurazione della dimensione del testo in /etc/freetds/freetds.conf.

ho usato

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

e tutto ha funzionato bene.

Strano è ciò che FreeTDS documentation says sull'opzione di configurazione dimensione del testo:

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.

La configurazione dice anche che il valore massimo (e il valore predefinito) è 4,294,967,295. Tuttavia, quando provo ad usare quel valore nella query ottengo un errore, il numero massimo che potrei usare nella query è 2.147.483.647 (metà).

Da quella spiegazione ho pensato che solo l'impostazione di questa opzione di configurazione sarebbe stata sufficiente. Risulta che ho sbagliato, l'impostazione di TEXTSIZE nella query ha risolto il problema.

Di seguito si riporta il codice di lavoro completo:

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

penso che si dovrebbe utilizzare un'istanza pyodbc.Binary per avvolgere i dati:

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

Recupero dovrebbe essere

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

UPDATE: Il problema è in inserimento. Cambia la tua SQL inserimento al seguente:

"""DECLARE @txtptr varbinary(16) 

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

Ho anche aggiornato l'errore che ho fatto nella utilizzando l'attributo valore nel codice di recupero.

Con questa modifica, sono in grado di inserire e recuperare un'immagine JPEG 320K nel database (i dati recuperati sono identici ai dati inseriti).

N.B. Il tipo di dati image è obsoleto e sostituito da varbinary(max) nelle versioni successive di SQL Server. La stessa logica per l'inserimento/recupero dovrebbe applicarsi, tuttavia, per il tipo di colonna più recente.

+0

Corretto, anche se ho dovuto usare str (cur.fetchall() [0] [0]) per ottenere i dati. .value restituito: AttributeError: l'oggetto 'buffer' non ha attributo 'valore' – nosklo

+0

Ho ancora problemi. Ho aggiornato la domanda, per favore dai un'occhiata. – nosklo

+0

Thans Vinay per l'aggiornamento. Risulta che il problema non è in fase di inserimento. Ho provato il codice TEXTPTR e ho ottenuto lo stesso risultato. Grazie per il tuo tempo. Ho modificato di nuovo la domanda per mostrare il problema corrente. Puoi darmi il codice completo che hai detto che funzionava? Puoi darmi dettagli sulla tua configurazione? Non riesco ancora a farlo funzionare. Se non ti sta chiedendo molto, puoi anche eseguire il mio codice invariato nella configurazione e comunicarmi i risultati? L'ho reso disponibile su http://paste.pocoo.org/show/125955/ e utilizza un file su Internet, quindi funzionerà esattamente allo stesso modo. – nosklo

1

Ho avuto un simile problema 4096 troncamento su TEXT campi, che SET TEXTSIZE 2147483647 fisse per me, ma anche questo problema è stato risolto per me:

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