Storing Images with LOAD_FILE Storing Images Using a Script

gain som e t able lookup speed by st oring im ages in t he filesyst em and using fix- lengt h t ypes for t he colum ns in t he im age t able.

17.7.4 Storing Images with LOAD_FILE

The LOAD_FILE funct ion t akes an argum ent indicat ing a file t o be read and st ored in t he dat abase. For exam ple, an im age st ored in t m p m yim age.png m ight be loaded int o a t able like t his: mysql INSERT INTO mytbl image_data VALUESLOAD_FILEtmpmyimage.png; To load im ages int o MySQL wit h LOAD_FILE , cert ain requirem ent s m ust be sat isfied: • The im age file m ust be locat ed on t he MySQL server host . • The file m ust be readable by t he server. • You m ust have t he FILE pr ivilege. These const raint s m ean t hat LOAD_FILE is available only t o som e MySQL users.

17.7.5 Storing Images Using a Script

I f LOAD_FILE is not an opt ion or you dont want t o use it , you can writ e a short program t o load your im ages. The program should eit her read t he cont ent s of an im age file and creat e a record t hat cont ains t he im age dat a, or creat e a record t hat indicat es where in t he filesyst em t he im age file is locat ed. I f you elect t o st ore t he im age in MySQL, you include t he im age dat a in t he record-creat ion st at em ent t he sam e way as any ot her kind of dat a. That is, you eit her use a placeholder and bind t he dat a value t o it , or else encode t he dat a and put it direct ly int o t he query st ring. The script shown in t his sect ion, st ore_im age.pl, runs from t he com m and line and st ores an im age file for lat er use. The script t akes no side in t he debat e over whet her t o st ore im ages in t he dat abase or t he filesyst em . I nst ead, it dem onst rat es how t o im plem ent bot h approaches Of course, t his t akes double t he st orage space, so t o adapt t his script for your own use, youll want t o ret ain only t he part s t hat are appropriat e for whichever st orage m et hod you want t o im plem ent . The necessary m odificat ions are discussed at t he end of t his sect ion. The st ore_im age.pl script uses an image t able t hat includes colum ns for t he im age I D, nam e, and MI ME t ype, and a colum n in which t o st ore t he im age dat a: CREATE TABLE image id INT UNSIGNED NOT NULL AUTO_INCREMENT, image ID number name VARCHAR30 NOT NULL, image name type VARCHAR20 NOT NULL, image MIME type data MEDIUMBLOB NOT NULL, image data PRIMARY KEY id, id and name are unique UNIQUE name ; The name colum n indicat es t he nam e of t he im age file in t he direct ory where im ages are st ored in t he filesyst em . The data colum n is a MEDIUMBLOB , which is good for im ages sm aller t han 16 MB. I f you need larger im ages, use a LONGBLOB colum n. I t is possible t o use t he name colum n t o st ore full pat hnam es t o im ages in t he dat abase, but if you put t hem all under t he sam e direct ory, you can st ore nam es t hat are relat ive t o t hat direct ory and name values w ill t ake less space. st ore_im age.pl does t his, but of course it needs t o know t he pat hnam e of t he im age st orage direct ory. That s what t he image_dir variable is for. You should check t his variables value and m odify it as necessary before running t he script . The default value reflect s where I like t o st ore im ages, but youll need t o change it according t o your own preferences. Make sure t o creat e t he direct ory if it doesnt exist before you run t he script . Youll also need t o check and possibly change t he im age direct ory pat hnam e in t he display_im age.pl script discussed lat er in t his chapt er. st ore_im age.pl looks like t his: usrbinperl -w store_image.pl - read an image file, store in that image table and in the filesystem. Normally, youd store images only in one place or another; this script demonstrates how to do both. use strict; use lib qwusrlocalapachelibperl; use Fcntl; for O_RDONLY, O_WRONLY, O_CREAT use FileHandle; use Cookbook; Default image storage directory and pathname separator CHANGE THESE AS NECESSARY my image_dir = usrlocalapachehtdocsmcbimages; my path_sep = ; Reset directory and pathname separator for WindowsDOS if O =~ MSWini || O =~ dos { image_dir = D:\\apache\\htdocs\\mcb\\images; path_sep = \\; } -d image_dir or die 0: image directory image_dir\ndoes not exist\n; Print help message if script was not invoked properly ARGV == 2 || ARGV == 3 or die EOF; Usage: 0 image_file mime_type [image_name] image_file = name of the image file to store mime_time = the image MIME type e.g., imagejpeg or imagepng image_name = alternate name to give the image image_name is optional; if not specified, the default is the image file basename. EOF my file_name = shift ARGV; image filename my mime_type = shift ARGV; image MIME type my image_name = shift ARGV; image name optional if image name was not specified, use filename basename allow either or \ as separator image_name = file_name =~ s|.[\\]|| unless defined image_name; my fh = new FileHandle; my size, data; sysopen fh, file_name, O_RDONLY or die Cannot read file_name: \n; binmode fh; helpful for binary data size = stat fh[7]; sysread fh, data, size == size or die Failed to read entire file file_name: \n; fh-close ; Save image file in filesystem under image_dir. Overwrite file if an old version exists. my image_path = image_dir . path_sep . image_name; sysopen fh, image_path, O_WRONLY|O_CREAT or die Cannot open image_path: \n; binmode fh; helpful for binary data syswrite fh, data, size == size or die Failed to write entire image file image_path: \n; fh-close ; Save image in database table. Use REPLACE to kick out any old image with same name. my dbh = Cookbook::connect ; dbh-do REPLACE INTO image name,type,data VALUES?,?,?, undef, image_name, mime_type, data; dbh-disconnect ; exit 0; I f you invoke t he script wit h no argum ent s, it displays a short help m essage. Ot herwise, it requires t wo argum ent s t hat specify t he nam e of t he im age file and t he MI ME t ype of t he im age. By default , t he files basenam e final com ponent is also used as t he nam e of t he im age st ored in t he dat abase and in t he im age direct ory. To use a different nam e, provide it using an opt ional t hird argum ent . The script is fairly st raight forward. I t perform s t he following act ions: 1. Check t hat t he proper num ber of argum ent s was given and init ialize som e variables from t hem . 2. Make sure t he im age direct ory exist s. I f it does not , t he script cannot cont inue. 3. Open and read t he cont ent s of t he im age file. 4. St ore t he im age as a file in t he im age direct ory. 5. St ore a record cont aining ident ifying inform at ion and t he im age dat a in t he image t able. st ore_im age.pl uses REPLACE rat her t han INSERT so t hat you can replace an old im age wit h a new version having t he sam e nam e sim ply by loading t he new one. The query specifies no id colum n value; id is an AUTO_INCREMENT colum n, so MySQL assigns it a unique sequence num ber aut om at ically. Not e t hat if you replace an im age by loading a new one wit h t he sam e nam e as an exist ing im age, t he REPLACE st at em ent will generat e a new id value. I f you want t o keep t he old value, you should issue a SELECT first t o see if t he nam e already exist s, t hen m odify t he REPLACE t o specify t he exist ing id value if a record was found, and NULL ot herw ise. The REPLACE st at em ent t hat st ores t he im age inform at ion int o MySQL is relat ively m undane: dbh-do REPLACE INTO image name,type,data VALUES?,?,?, undef, image_name, mime_type, data; I f you exam ine t he st at em ent looking for som e special indicat or of how t o handle binary dat a, youll be disappoint ed, because t he data variable t hat cont ains t he im age isnt t reat ed as special in any way. The query refers t o all colum n values uniform ly using ? placeholder charact ers and t he values are passed at t he end of t he do call. Anot her w ay t o accom plish t he sam e result is t o perform escape processing on t he colum n values explicit ly and t hen insert t hem direct ly int o t he query st ring: image_name = dbh-quote image_name; mime_type = dbh-quote mime_type; data = dbh-quote data; dbh-do REPLACE INTO image name,type,data VALUESimage_name,mime_type,data; Many people m ake im age- handling a lot m ore t roublesom e t han it really is. I f you properly handle im age dat a in a query by using placeholders or by encoding it , youll have no problem s. I f you dont , youll get errors. Sim ple as t hat . This is no different t han how you should handle ot her kinds of dat a, even t ext . Aft er all, if you insert int o a query a piece of t ext t hat cont ains quot es or ot her special charact ers wit hout escaping t hem , t he query will blow up in your face. So t he need for placeholders or encoding is not som e special t hing t hat s necessary only for im ages—it s necessary for all dat a. Say it wit h m e: I will always use placeholders or encode m y colum n values. Always. Always, always, always. Having said t hat , I feel obliged t o point out t hat if you know enough about a given value—for exam ple, if youre absolut ely cert ain t hat it s an int eger—t here are t im es you can get away wit h breaking t his rule. Nevert heless, it s never wrong t o follow it . To t ry out t he script , change locat ion int o t he apache im ages direct ory of t he recipes dist ribut ion. This direct ory cont ains t he st ore_im age.pl script , and som e sam ple im ages are in it s flags subdirect ory t heyre pict ures of nat ional flags for several count ries . To load one of t hese im ages, run t he script like t his under Unix: .store_image.pl flagsiceland.jpg imagejpeg Or like t his under Windows: C:\ store_image.pl flags\iceland.jpg imagejpeg st ore_im age.pl t akes care of im age st orage, and t he next sect ion discusses how t o ret rieve im ages t o serve t hem over t he Web. What about delet ing im ages? I ll leave it t o you t o writ e a ut ilit y t o rem ove im ages t hat you no longer want . I f you are st oring im ages in t he filesyst em , rem em ber t o delet e bot h t he dat abase record and t he im age file t hat t he record point s t o. st ore_im age.pl st ores each im age bot h in t he dat abase and in t he filesyst em for illust rat ive purposes, but of course t hat m akes it inefficient . Earlier, I m ent ioned t hat if you use t his script as a basis for your own applicat ions, you should m odify it t o st ore im ages only in one place— eit her in t he dat abase or in t he filesyst em —not in bot h places. The m odificat ions are as follows: • To adapt t he script t o st ore im ages only in MySQL, dont define an im age direct ory and delet e t he code t hat checks for t hat direct orys exist ence and t hat writ es im age files t here. • To adapt t he script for st orage only in t he filesyst em , drop t he data colum n from t he image t able, and m odify t he REPLACE st at em ent so it doesnt refer t o t hat colum n. These m odificat ions also apply t o t he display_im age.pl im age processing script shown in Recipe 17.8 .

17.7.6 See Also