.\" Automatically generated by Pod::Man 2.27 (Pod::Simple 3.28) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{ . if \nF \{ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "CGI::Uploader::Cookbook 3pm" .TH CGI::Uploader::Cookbook 3pm "2011-02-02" "perl v5.18.1" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" CGI::Uploader::Cookbook \- Examples of CGI::Uploader usage .SH "Description" .IX Header "Description" \&\f(CW\*(C`CGI::Uploader::Cookbook\*(C'\fR is a tutorial that accompanies the \fBCGI::Uploader\fR distribution. It shows example syntax for common uses. .PP \&\f(CW\*(C`CGI::Uploader\*(C'\fR module is designed to help with the task of managing files uploaded through a \s-1CGI\s0 application. The files are stored on the file system, and the file attributes stored in a \s-1SQL\s0 database. .SH "Introduction to CGI::Uploader" .IX Header "Introduction to CGI::Uploader" .SS "A Little History" .IX Subsection "A Little History" The release of this module represents a culmination of seven years of experience managing file uploads as a professional website developer for Summersault, \s-1LLC \s0(). Over that time I noticed patterns that were re-usable from project to project. I went through several versions and rewrites of modules that attempted to be 'generic' and not need modification when the next project came along. With CGI::Uploader, I believe I finally have a solution that I will continue to be happy with and I think others will be find generally useful. Enjoy! .SS "Freedom of Choice" .IX Subsection "Freedom of Choice" I endeavored to make CGI::Uploader to work within a variety of system designs. It offers you freedom choice in the following areas: .IP "\(bu" 4 Database Choice .Sp MySQL and Postgres are supported directly. The \s-1SQL\s0 used is very simple\*(-- support for additional databases should be trivial. .IP "\(bu" 4 Choice of Query Provider .Sp The query object used may provided by \f(CW\*(C`CGI.pm\*(C'\fR, \f(CW\*(C`CGI::Simple\*(C'\fR or \&\f(CW\*(C`Apache::Request\*(C'\fR. Another source could be used by overriding the \f(CW\*(C`upload\*(C'\fR method. .IP "\(bu" 4 File Storage Schemes for Large and Small Projects .Sp For small projects, all uploads can be stored in a single directory. For large projects, we provide the \f(CW\*(C`md5\*(C'\fR file scheme, which should scale well to millions of images, without burdening any single directory with storing too many of them. .IP "\(bu" 4 Choice of Data Display .Sp Because the meta data is stored in a straightforward \s-1SQL\s0 database table, you can retrieve your data and display in any number of custom ways. Several functions are also built in to help with common display tasks. The \f(CW\*(C`build_loc()\*(C'\fR method is used to construct the file system or \s-1URL\s0 path of an image, given it's \s-1ID\s0 and extension. .Sp \&\f(CW\*(C`fk_meta()\*(C'\fR provides an easy way to get the meta data of an upload by relating it to a foreign key in another table. .Sp Finally, \f(CW\*(C`transform_meta()\*(C'\fR is a basic function which transforms a hashref of data from the database into a format more useful for display, producing a hash that looks like this: .Sp .Vb 6 \& { \& my_custom_prefix_id => 523, \& my_custom_prefix_url => \*(Aqhttp://localhost/images/uploads/523.pdf\*(Aq, \& my_custom_prefix_width => 23, \& my_custom_prefix_height => 46, \& } .Ve .IP "\(bu" 4 Image Processor .Sp While \f(CW\*(C`CGI::Uploader\*(C'\fR works with all types of file uploads, it contains a number of features to help with common tasks associated with image uploads. .Sp \&\f(CW\*(C`Image::Magick\*(C'\fR is the preferred image processing module for to use when creating the thumbnails. Support for \f(CW\*(C`GD\*(C'\fR is in progress. \f(CW\*(C`GD\*(C'\fR supports many fewer formats, but also has much fewer dependencies to get installed than \&\f(CW\*(C`Image::Magick\*(C'\fR does. Another providers could be used by extending or overriding the \f(CW\*(C`gen_thumb()\*(C'\fR method. .SS "Just Three Essential Methods to Learn" .IX Subsection "Just Three Essential Methods to Learn" A goal of is to provide a high-level interface to make managing file uploads easy. Only three methods are needed to manage all the functions needed to store, update, delete and view the uploads attached to some database entity. Those methods are \f(CW\*(C`store_uploads()\*(C'\fR, \f(CW\*(C`delete_checked_uploads()\*(C'\fR and \f(CW\*(C`fk_meta\*(C'\fR. .SS "More methods when you need them" .IX Subsection "More methods when you need them" When your needs before more complex, you can call the lower-level functions in \f(CW\*(C`CGI::Uploader\*(C'\fR to meet your needs. Most functions use file names to access file uploads, so it's easy to use the module to manipulate files from other sources than the browser upload field. .PP For example, the \f(CW\*(C`gen_thumb()\*(C'\fR method is general purpose thumbnail creating routine. .SH "Browse, Read, Edit, Add, Delete (BREAD) Example Application" .IX Header "Browse, Read, Edit, Add, Delete (BREAD) Example Application" The following sections will provide a walk through of a simple application using CGI::Uploader. This is intended to provide the picture of how this module can be used. Some details have been glossed over. For a complete, working example application, please see the \f(CW\*(C`examples\*(C'\fR directory of the distribution. .PP Before \f(CW\*(C`CGI::Uploader\*(C'\fR can be useful, some setup needs to be done. You need some database tables to store the information in. .SS "Example Database" .IX Subsection "Example Database" For these examples, we'll set up some tables to manage photos of friends. Here is the \s-1SQL\s0 to create such tables with Postgres: .PP .Vb 11 \& \-\- Note the Postgres specific syntax here \& CREATE SEQUENCE upload_id_seq; \& CREATE TABLE uploads ( \& upload_id int primary key not null \& default nextval(\*(Aqupload_id_seq\*(Aq), \& mime_type character varying(64), \& extension character varying(8), \-\- file extension \& width integer, \& height integer, \& gen_from_id integer \& ); \& \& CREATE SEQUENCE friend_id_seq; \& CREATE TABLE address_book ( \& friend_id int primary key NOT NULL DEFAULT nextval(\*(Aqfriend_id_seq\*(Aq), \& full_name varchar(64), \& \& \-\- these two reference uploads(\*(Aqupload_id\*(Aq), \& photo_id int, \& photo_thumbnail_id int \& ); .Ve .PP (\fIMySQL\fR is also supported. Check in the distribution for sample \s-1SQL \s0'Create' scripts for both \fIMySQL\fR and \fIPostgresql\fR databases). .SS "Object Creation" .IX Subsection "Object Creation" You can create one \f(CW\*(C`CGI::Uploader\*(C'\fR object and use it for adding, updating, viewing and deleting uploads. So don't despair that it has a few required parameters\*(-- you only need to type them once! :) .PP .Vb 9 \& use CGI::Uploader::Transform::ImageMagick; \& my $u = CGI::Uploader\->new( \& spec => { \& photo => { \& gen_files => { \& photo_thumbnail => gen_thumb({ w => 100, h => 100}), \& } \& } \& } \& \& updir_url => \*(Aqhttp://localhost/uploads\*(Aq, \& updir_path => \*(Aq/home/friends/www/uploads\*(Aq, \& dbh => $dbh, \& \& ); .Ve .SH "Adding a Database Record and Related Uploads" .IX Header "Adding a Database Record and Related Uploads" Before we can do anything else with the uploads, we need to get some added into the system. .PP \&\f(CW\*(C`CGI::Uploader\*(C'\fR is designed to make this happening easily as part of the normal process of adding a normal database record. In this case, we'll be adding a friend. .SS "Example 'Add Form'" .IX Subsection "Example 'Add Form'" Here's the form used to add a friend. It includes fields for the friend's name, and a photo of them. .PP .Vb 5 \&
\& Friend Name:
\& Image: \& \&
.Ve .PP Notice that the 'enctype' is important for file uploads to work. .PP Notice we have a text field for a 'full_name' and a file upload field named \&'photo'. .SS "Processing the Add Form" .IX Subsection "Processing the Add Form" \&\s-1AS\s0 a first step for processing the 'add form', I recommend validating the form with Data::FormValidator. It includes several routines just to validate file uploads. However, it's not necessary to validate the form. .PP .Vb 5 \& # CGI::Simple provides a CGI.pm\-like interface with much better performance \& use CGI::Simple; \& my $q = CGI::Simple\->new(); \& my $form = $q\->Vars; \& my $friend = $u\->store_uploads($form); \& \& # Now the $friend hash been transformed so it can easily inserted \& # It now looks like this: \& # { \& # full_name => \*(AqM. Lewis\*(Aq, \& # photo_id => 3, \& # photo_thumbnail_id => 4, \& # } \& \& # I like to use SQL::Interp for easy inserts. \& # See DBIx::Simple for an even more friendly wrapper. \& use SQL::Interp \*(Aqsql_interp\*(Aq; \& $dbh\->do(sql_interp "INSERT INTO address_book",$friend); .Ve .SS "Database Result of Adding" .IX Subsection "Database Result of Adding" Here's what ended up in the database: .PP .Vb 1 \& address_book table: \& \& friend_id | full_name | photo_id | photo_thumbnail_id \& \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\- \& 2 | M. Lewis | 3 | 4 \& \& \& uploads table: \& \& upload_id | mime_type | extension | width | height | gen_from_id \& \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\- \& 3 | image/png | .png | 200 | 400 | \& 4 | image/png | .png | 50 | 100 | 3 .Ve .PP The files are stored on the file system. '4.png' was generated on the server a thumbnail of 3.png. .PP .Vb 2 \& /home/friends/www/uploads/3.png \& /home/friends/www/uploads/4.png .Ve .SH "Displaying & Linking to Uploads" .IX Header "Displaying & Linking to Uploads" You don't strictly need this module to display the uploaded image. You could construct your own database queries and URLs instead. However, the \f(CW\*(C`fk_meta\*(C'\fR method is provided to simplify things for you. .PP Continuing with the example above, we would use this code to generate the details we need to display and link to the photo and thumbnail: .PP .Vb 5 \& my $href = $u\->fk_meta( \& table => \*(Aqaddress_book\*(Aq, \& where => { friend_id => 2 }, \& prefixes => [qw/photo photo_thumbnail/], \& ); .Ve .PP That will fetch the details of the photo and thumbnail associated with the friend who is an \&\s-1ID\s0 of \*(L"2\*(R". .PP The resulting hashref will look something like this: .PP .Vb 5 \& { \& photo_id => 3, \& photo_url =>\*(Aqhttp://localhost/uploads/3.png?23\*(Aq, \& photo_width => 200, \& photo_height => 400\*(Aq, \& \& photo_thumbnail_id => 4, \& photo_thumbnail_url =>\*(Aqhttp://localhost/uploads/4.png?23\*(Aq, \& photo_thumbnail_width => 50, \& photo_thumbnail_height => 200\*(Aq, \& \& } .Ve .PP This hashref can often be passed directly to a templating system such as HTML::Template for display. .PP You may be wondering about the query strings on the \s-1URLS.\s0 These are random numbers to defeat browser image caching, which is very useful on \*(L"edit\*(R" forms. This behavior may change or become optional in a future release. .SH "Displaying an Update Form" .IX Header "Displaying an Update Form" So now we've added 'M. Lewis' to our friend database and displayed his photo on the web. M. Lewis turned out not to be happy about this. He reports that the photo used was not his 'good side' and has sent a 'better' photo to use. .PP So now we need to have a form to update the photo from. .PP The form to update the upload will be a lot like the 'add form'. Additionally, it's nice to display a link to current upload on the form. This can be done using \f(CW\*(C`fk_meta\*(C'\fR, as demonstrated above. .PP Our Update Form might look like this if we are using HTML::Template for display: .PP .Vb 10 \&
\&

Friend Name:

\&

\& Current Image
\& Delete Image? \&

\& \&

Image:

\& \&
.Ve .SS "Processing an Update Form" .IX Subsection "Processing an Update Form" Processing an update form is the most complicated part of application. From this form it's possible to add, update and delete uploads .PP To process the update form, we'll first delete any uploads that the user has requested to remove. Next, add and update any other uploads as need. .PP .Vb 1 \& my $friend = $q\->Vars; \& \& my @fk_names = $u\->delete_checked_uploads; \& map { $friend\->{$_} = undef } @fk_names; \& delete $friend\->{photo_delete}; \& \& $friend = $u\->store_uploads($friend); .Ve .PP Although the call to \f(CW\*(C`store_uploads()\*(C'\fR looks the same as it did for adding a record, it works a little different now. Notice we passed a photo_id through the form above. Because this is present, that record will be updated instead of creating a new one. .SH "Recipe Idea: Put an existing directory of photos on line" .IX Header "Recipe Idea: Put an existing directory of photos on line" You have an existing directory full of \s-1JIGS\s0 that you want to put on-line as a photo gallery, with medium and small versions created of all the images. \&\f(CW\*(C`CGI::Uploader\*(C'\fR is versatile enough to help in this situation as well. .PP Your spec might look like this: .PP .Vb 4 \& large_jpeg => [ \& { name => \*(Aqmedium\*(Aq, w => 500, }, \& { name => \*(Aqsmall\*(Aq, w => 250, \& ], .Ve .PP From there, read in all the file names and store all the files, with the smaller versions being created automatically for you along the way. .PP .Vb 8 \& for my $jpeg (<*.jpg>) { \& my %entity_upload_extra = $self\->store_upload( \& file_field => \*(Aqlarge_jpeg\*(Aq, \& src_file => $jpeg, \& uploaded_mt => \*(Aqimage/jpeg\*(Aq, \& file_name => $jpeg, \& ); \& } .Ve .PP Now you may want to display a page containing all of the smallest thumbnails. If these IDs had been stored in another table, we could use \fIfk_meta()\fR to get all of the small thumbnails. .PP In this case, it is still possible to get a reasonable result by selecting images based on their size. .PP [\s-1TODO:\s0 example code for this needs to be written. ] .SH "Recipe Idea: Handling anonymous image uploads" .IX Header "Recipe Idea: Handling anonymous image uploads" It is also possible with CGI::Uploader to have many \*(L"anynonmous\*(R" uploads associated with another entity in the database. .PP [ \s-1TODO:\s0 And the documentation for how to that still needs to be written. :) ] .SH "See Also" .IX Header "See Also" CGI::Uploader .SH "Author" .IX Header "Author" .Vb 1 \& Mark Stosberg .Ve