1.
Sending image files via CGI 2.
An example implementation 3.
How to install 4.
References
by Jürgen Hermann
last updated 2000/01/31
(version 1.1)
also available as XML
CGI Bits & Pieces is a new column in which I will present
bits and pieces on different CGI-related issues. If you are a CGI novice,
see the references at the end of this article. Having read
Brent's article is the bare minimum to understand anything that follows.
All examples are implemented in C++ and usually compiled and tested on both
Windows NT4 using Visual C++ 5.0 and FreeBSD using gcc.
| |
In this installment, we want to send an image file via CGI to the browser.
You might ask, and rightly so, what is the purpose of this since any web server
can send images by itself, without the need for a cgi-bin program.
Well, there are several valid reasons. For one, you might store your
images in a modern RDBMS (relational database management system)
that supports BLOBs (binary large objects). To get your images
to the user's browser, you obviously have to retrieve them from the
database server first, which no current httpd will do for you.
Another reason is that the image file does not have to be in your
public WWW area, so you can send any file in the filesystem the web server (or actually,
your cgi-bin) has access to. This of course can also be a security hole, and
in the example source presented here this is not checked. If you use it for
anything real, add some code to replace all slashes with underscores or something similar
that prevents opening up your file system.
Other typical applications are images that are generated
on the fly, for example by using ImageMagick.
Actual examples on the web are statistical graphs of web server usage, or the infamous GIF counters.
Further, banner type programs contain an algorithm that decides to send one of several possible images;
this might serve as a exercise for the reader, extend the program to send the file bgndX.gif ,
where X is a random number between 0 and 9. Then use that modified program in the background
image URL of a web page
| | | |
<BODY BACKGROUND="/cgi-bin/randomimage">
| | | | |
and you get a nice effect when you hit "Reload".
Finally, sending binary data instead of the usual text/plain
or text/html responses gives us the chance to see what is necessary to do
that on a Windows system. If you approach the task in a naive way, you will get
broken image data because of the CP/M and MSDOS heritage still lurking in the dusty corners
of your Windows OS. Namely, by default any line feed (LF, '\n' ) you send
to standard output is automatically converted to a CRLF pair, which is the standard
Windows line break marker. At the very start of main() , you can see
how we can prevent the runtime library from doing that.
|
| |
Let's get to the details of sending an image file via HTTP. The task at hand can be broken down
into three major pieces: getting the image name from the CGI environment, sending the necessary MIME headers,
and finally streaming the image data to the browser. You can find that structure reflected in the
main of the sample program.
To send the image file, we use a helper function named
dumpFile that opens a file and sends its contents to
the standard output stream.
| | | |
// Send an image file via CGI
//
// Copyright (c) 1998 by Jürgen Hermann, All Rights Reserved.
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <iostream.h>
#include <fstream.h>
#if _WIN32
#include <fcntl.h>
#include <io.h>
#define strcasecmp stricmp
#endif
void cgiError()
{
// Most simple error handling
cout << endl;
exit(0);
}
void dumpFile(const char* filename)
{
ifstream file(filename, ios::in | ios::binary);
if (file) {
while (!file.eof()) {
char buf[1024];
file.read(buf, sizeof(buf));
int count = file.gcount();
if (count) {
cout.write(buf, count);
} else {
break;
}
}
cout.flush();
}
}
int main()
{
#if _WIN32
// Standard I/O is in text mode by default; since we intend
// to send binary image data to standard output, we have to
// set it to binary mode.
// Error handling is tricky to say the least, so we have none.
_fmode = _O_BINARY;
if (_setmode(_fileno(stdin), _fmode) == -1) {}
if (_setmode(_fileno(stdout), _fmode) == -1) {}
#endif
// First, we get the filename of the image to send
char* querystr = getenv("QUERY_STRING");
char* imgparm = querystr ? strstr(querystr, "image=") : 0;
char imagename[81];
if (!imgparm || 1 != sscanf(imgparm, "image=%80[^&]", imagename)) {
cgiError();
}
// Get the image type and send the headers
const char* imgtype = "gif"; // assume gif by default
char* ptr = strrchr(imagename, '.');
if (ptr && (strcasecmp(ptr, ".jpg") == 0 || strcasecmp(ptr, ".jpeg") == 0)) {
imgtype = "jpeg";
}
cout << "Content-Type: " << "image/" << imgtype << endl;
cout << endl;
// Send the image
dumpFile(imagename);
return 0;
}
| | | | |
|
| |
Gundavaram, Shishir, CGI Programming on the World Wide Web.
O'Reilly, 1996, 433 p., covers CGI basics as well as hot topics like data base interfacing, details of the HTTP protocol and their use, forms, server side includes, hyper-media, client-pull and server-push (examples in Perl), ISBN 1-56592-168-2.
York, Brent, CGI in C - A starter's tutorial.
C-Scene, 1997.
|
This article is Copyright © 1998 by Jürgen Hermann
and Copyright © 1998 by C-Scene. All Rights Reserved.
|