python <--> MySQL w.r.t. binary files (bitmap images like .jpg's)
richard at insight20.freeserve.co.uk
richard at insight20.freeserve.co.uk
Wed Jun 14 14:40:48 EDT 2000
Hi
fairly new to python - hope this a helpful post
set context - here's a message I sent -
"Hi all
Got the ans to the issue of loading binaries, which includes bitmap
image files like .jpg's and .png's, into the MySQL database.
Proves that the Python/MySQL pairing makes it simple (touch wood).
For those of you less familiar, MySQL is a relational database. Ok -
slight qualification - the relational database is the data, organised
in "tables" which relate to eachother, all regarded as a single entity
known as "the database". (on a unix file system (and other current
platforms) the "database" is represented by a directory and the
"tables" by individual files within that directory - there are also
other files which are maintained by the database *program* to
facilitate indexing etc)
Have slightly "jumped the gun" in making the last distinction - MySQL
is a *database program* which manages and operates upon databases and
provides access into the databases for the user (the program can
oversee many databases).
So, loading binaries to the database... given that there is a datatype
"BLOB" (TINYBLOB (256Bytes?) up to and including LONGBLOB for binary
data up to 4GB)...
The interface to the MySQL database program is the "mysql" monitor
(that's what you start-up when you type "mysql" at the command line) -
it starts-up and you identify yourself to the database program. You
can also reach into the database from programming languages via
modules written for the language to interface with database program
through "calls" provided for this purpose by the database program.
The problem I ran into was that binary files can "accidentally appear
to have" control characters which trigger events (unwanted) in the db
interface. For instance in a .jpg, you are going to at some point
have a sequence of bits at some multiple of 8bit (1byte) offset from
the beginning of the file which reads 00001010, which has no relation
at all to the ASCII "newline" character, which in the ASCII character
set has this bit pattern (it's represented by the hexadecimal number
0x0A, which if you convert that to binary is the bit sequence I have
just quoted). There are other bit patterns which "appear" the same as
other ASCII control characters. The single-quote and double-quote
characters also cause problems to the "mysql" MySQL monitor and/or the
interface.
What you need to do is to "escape" these bit patterns in the binary
file by inserting the slash character before these "problem"
bit-patterns (they are not seen when the binary file is recovered from
MySQL).
Jumping straight to the answer - (correct me if I am wrong on the
location of this function) the MySQL API (Application Programming
Interface) provides the function "mysql_escape_string()" which
performs the "escaping" of the characters which would otherwise cause
the interface problems during reading-in. This has been "wrappered"
in the MySQLdb-0.2.1 module which abstracts the API as a set of Python
objects. It is in the _mysql module, which you would not normally use
directly because it presents a fundamental API which is in general
abstracted into higher level API's so that your SQL is transportable
between databases. However, this is an application-specific problem,
and the answer is application specific. So, let's rock...
In patchy pieces of Python (complete listing of an example follows)
import _mysql
import MySQLdb
(connect to database)
(create a cursor object)
("open" your binary (eg. (bitmap) image) file and get a file handle
object)
(build a string of the SQL you want to "execute" which looks like this
sqlstr="update ... set ...='" + _mysql.escape_string(imgfile.read())
+"' where ...")
cursor_obj.execute(sqlstr)
That's it - the binary is in the database.
When you "select" the thing, you get it back as the binary pre- the
"escaping" operation.
Have fun.
Here's the example code listing...
Richard Smith
------------------------------------------------------------------------
#!/usr/local/bin/python
import _mysql
# MySQL (ie. implementation) specific - but fairly
# implementation-specific problem
import MySQLdb
db=MySQLdb.connect(db="cheeses",user="a_user",passwd="a_passwd")
c=db.cursor()
imgfile=open("a_cheese.png",'rb')
## imgvar=imgfile.read() # avoid this
# put SQL strings in place
# "examine_str"
sql_db_view="select * from cheese_name where cheese_name_pk=1"
# reset
sql_reset_img1="update cheese_name set picture_img=NULL where cheese_name_pk=1"
# specifically retrieve the image field
sql_db_image_view="select picture_img from cheese_name where cheese_name_pk=1"
# "do it" SQL command - the "update" loading of the image file into the database
sqlstr="update cheese_name set picture_img='" + _mysql.escape_string(imgfile.read()) +"' where cheese_name_pk=1"
# do reset...
c.execute(sql_reset_img1)
# do the "update" load...
c.execute(sqlstr)
# look at what the result is...
c.execute(sql_db_image_view)
# do file write
f_out=open('tmp.img.png','wb')
f_out.write(c.fetchone()[0])
# flush the cache / buffer whatever now
f_out.close()
# other tidy-ups
imgfile.close()"
To which I got one answer...
"> import _mysql
> import MySQLdb
> (connect to database)
> (create a cursor object)
> ("open" your binary (eg. (bitmap) image) file and get a file handle
> object)
> (build a string of the SQL you want to "execute" which looks like this
> sqlstr="update ... set ...='" + _mysql.escape_string(imgfile.read())
> +"' where ...")
> cursor_obj.execute(sqlstr)
If you were using Oracle and Pro*C, you would do something like:
EXEC SQL UPDATE ... set column=:value
where ':value' represents a C variable containing a value. The value in the
C variable isn't parsed by the SQL parser and needn't be escaped.
Perl's DBI and DBD interface has something similar, though I can't remember
exactly how it's done.
I would be amazed if there wasn't a similar binding step in Python's
database interface (though, I admit, it does sound less well thought out
than the Perl one, which has a standard front-end and a variety of back-ends
for different databases). You should probably use this rather than taking a
large binary file, converting it (effectively) to text, forcing the parser
to parse it and convert it back to binary."
(from a very helpful much appreciated character)
So - is there a neater way of doing this - such as in line with the
hint of the (very much appreciated) respondent?
Richard Smith
More information about the Python-list
mailing list