Howto upload files with Tntnet
This document describes, how to upload files with Tntnet.
Sometimes there is a need for uploading files to the web application. This is done with a special html input element of type "file". The form element needs to specify an enctype of with the parameter "multipart/form-data". This enctype changes the way, form-data is sent to your webapplication. Luckily Tntnet handles all difficult stuff for you and fill your aruments specified in <%args>
sections.
But with upload files the situation is a little different. First the file-data might be quite large and in would be not the most efficient way to put the data into a std::string
like other arguments. The second problem is, that the file might have a additional attribute: the filename. Therefore Tntnet has a special API to handle this uploaded data.
The data comes in a special multipart structure, which is represented in Tntnet by the class tnt::Multipart
. This has references to all query parameters including uploaded files.
You get a const reference to this multipart object with the method getMultipart()
of your request object. Within this multipart object you can find your file with the method find(partName)
. The parameter partName is the same name, you gave your upload field in your html form. You get a tnt::Multipart::const_iterator
to the part. If the file is not found, the iterator points to the end-iterator retrieved with request.getMultipart().end()
. If found, the dereferenced iterator is a reference to a tnt::Part-object
, which represents your uploaded file. You can ask for the mime type with tnt::Part::getMimetype()
and for the filename with tnt::Part::getFilename()
.
The simplest way to fetch the data is to call tnt::Part::getBody()
. You get the data as a std::string
. But this is not the most efficient way, because Tntnet needs instantiate a std::string
and copy the data. There is a iterator interface for this. tnt::Part
defines a const_iterator
. Iterators to the start of your body is fetched with the getBodyBegin()
and part the end with getBodyEnd()
. If you don't need a std::string
this is more efficient.
This all sounds very complicated, but I hope it gets a little clearer, when you see a example.
The form looks like this:
<form method="post" enctype="multipart/form-data">
<input type="file" name="myfile">
<input type="submit">
</form>
And the code to process the uploaded file:
<%cpp>
const tnt::Multipart& mp = request.getMultipart();
tnt::Multipart::const_iterator it = mp.find("myfile");
if (it != mp.end())
{
// we found a uploaded file - write it to some upload-area
std::ofstream out("upload/" + it->getFilename());
out << it->getBody(); // this is less efficient, because a temporary std::string
// is created and the data is copied into it
// more efficient is the use of iterators:
for (tnt::Part::const_iterator pi = it->getBodyBegin(); pi != it->getBodyEnd(); ++pi)
out << *pi;
// ... or using STL-algorithm and ostreambuf_iterator:
std::copy(it->getBodyBegin(),
it->getBodyEnd(),
std::ostreambuf_iterator<char>(out));
}
</%cpp>
The application is a little online hexdumper for the web. The user can upload a file and see the first 1024 bytes as a hexdump.
You can find another example in the tntnet package in sdk/demos/upload.