So, I've been using tntnet for a while, and loving every moment of it.
It's a wonderful thing for web developers who want to harness the
amazing power of C++ without having to complicate things when outputting
straight HTML; a truly amazing synthesis of the two languages. To show
my thanks to the developer, and to the community, I'd like to give
something back: I'm going to share with everyone a great way to include
static content (such as images, stylesheets, javascript files, or
anything else that doesn't require any dynamic output) without having to
compile it into the program (which saves a lot of hassle by getting rid
of the need to use the binary compile switch in ecppc. This is
especially useful if you're creating a site with user-uploadable
images. (And because this is so much simpler, I think it might be a
nice idea to implement this into the base tntnet source somehow.) I
believe this would also be applicable to the recent thread on TinyMCE; I
haven't read it completely, but I've seen a lot of discussion on
compiling .js files... this method makes that unnecessary.
For this, you need two files, to add one ecpp file for each type of
static content you want to serve (css, txt, html, jpg, png, etc). The
reason for having different files for each is twofold: firstly, you need
to handle text-based content (html, javascript, css) differently from
binary content (images, flash files, java applets); secondly, you need
to manually set the MIME type for each file you send, because TNTnet
will set it to text/html by default. The great thing about this method,
though, is that, unlike the default method, you don't have to recompile
every time you make a tiny change to your CSS, which saves a lot of
development time, and you don't have to recompile every time you make a
slight change to an image file, or every time a new user uploads an
avatar, etc.
Firstly, style.ecpp. It's a fairly simple function, heavily commented
for your benefit:
<%pre>
#include <fstream>
</%pre>
<{
char c;
std::ifstream f;
/*
* Find out the file they're searching for.
* This will be a file relative to the current working directory (cwd).
* Can include folder names. Visiting www.yoursite.com/images/img.jpg
* will therefore reference images/img.jpg in your cwd, which typically
* will be the folder in which your executable is stored.
*/
string fname = request.getQuery();
// The filename will always have a preceding '/', so we want to
remove that to search in the cwd.
fname.erase(0, 1);
//Now that we've found the name of the file we want, we try to open it.
f.open (fname.c_str(), std::ios::in );
if(!f)
{
// If f is NULL, then the file doesn't exist; call whatever
"404" component you want, or just send a blank file.
callComp("notfound", request, reply, qparam);
}
else
{
//Set the content type properly - DON'T FORGET THIS, especially
for binary files!
reply.setContentType("text/css");
//Read the entire contents of the file, one char at a time, and
send it back out to the client.
while(f.good())
{
c = f.get();
if(f.good())
reply.out() << c;
}
}
// Close the file.
f.close();
}>
Now, that works great for text files, javascript files, css files, html
files... anything containing plain text. But if you try it with an
image file, you'll notice you get strange results. Only two lines of
code have to be changed, however, for image files to work.
Firstly, you need to change the f.open line to "f.open (fname.c_str(),
std::ios::in | std::ios::binary );". Secondly, you need to make sure
the content type is set properly (for png files, for example, it needs
to be "image/png." Other than that, the file can be copied identically.
You may ask, "Why not just put it all in a function like get_file(
string fname, string contentType, bool is_binary, tnt::HttpRequest
&request, tnt::HttpReply &reply, tnt::QueryParams &qparam)?" The answer
is that callComp is a protected member of the EcppComponent class, which
means there's no way to call it outside of the .ecpp file, since
everything within it is contained in a dynamically created class that
extends the EcppComponent class. You could make the function a boolean
and then call the component in the .ecpp file only if it returns false,
but when I tried to do that, the browser couldn't understand the output
as an image for the binary files. I'll continue looking into that and
see if I can figure out what I did wrong, or if it's just a fundamental
design flaw with that approach, and I'll send an update later.
Now, there's just one more thing that needs to be done: you need to map
"^/(.*).png$" to "png.ecpp", and likewise for other extensions -
"^/(.*).css$" to "style.ecpp", "^/(.*).jpg$" to "jpg.ecpp", etc.
I would also recommend using this as a stand-alone application (i.e.,
using a main.cpp and compiling an executable binary rather than relying
on the installed tntnet runtime) if you want your static content to be
in the same directory as the .ecpp files; otherwise, you'll probably
need to place your images in the same directory as tntnet.
Alternatively, you could place a "chdir( )" in all of your files to
ensure you go to the directory you want. (I haven't tested this, but
I'm assuming the cwd for the main tntnet file would be the base tntnet
directory. It's possible it switches the cwd, though; I don't really
feel like testing this, so someone else can give insight on this.)
And that's the scoop; with this, you should be able to easily handle
arbitrary inclusion of static content in your site, making
administration and modification of it a whole lot easier. Enjoy it!
Shadowcat
------------------------------------------------------------------------------
Let Crystal Reports handle the reporting - Free Crystal Reports 2008 30-Day
trial. Simplify your report design, integration and deployment - and focus on
what you do best, core application coding. Discover what's new with
Crystal Reports now. http://p.sf.net/sfu/bobj-july
_______________________________________________
Tntnet-general mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/tntnet-general