I recently had to develop an visualisation application. To make it easier to deploy, I chose to make it a C++ application with embedded web server. In that application, there is a need for the user to submit some data to be analyzed. Users have the choice to submit text directly (filling in a textarea) or selecting a file (using a file input element), both inputs are inserted in an HTML form. Since the form contains a file attachment, it has to be submitted in multipart/form-data encoding.
The form looks like this:
<form action="push" enctype="multipart/form-data" method="post"> <input id="logfile" name="logfile" type="file" /> <textarea cols="96" id="logtext" name="logtext" rows="10"></textarea> <select id="logtype" name="logtype">...</select> <input id="start-button" type="submit" value="Start Analysis" /> </form>
On the backend side, I used the Poco library to create the server application. To handle such form submission, the natural choice is to use Poco::Net::HTMLForm. And since file attachments are expected, it is required to provide a Poco::Net::PartHandler implementation.
My initial implementation grabbed the content of the submitted file using this code:
std::ofstream output(tmp.string());
output << contentStream.rdbuf() << std::flush;
output.close();
This is the classical implementation of a stream-to-stream copy. More details can be found here.
Unfortunately, Poco::Net::MultiPartReader provides content streams which are not totally conformant to the iostream protocol. As a result, the part handler consumes also the value of the next field, the logtext textarea. Strangely, the value of the last field, the logtype selection, was not consumed.
Looking around the net, I found out another writing in some Poco examples. So I wrote the following loop instead:
std::ofstream output(tmpPath.string());
char c = contentStream.get();
while (c != 0) { // example found had "while (c < 0)"
output << c;
c = contentStream.get();
}
output.flush();
output.close();
This was much better. The next field would not be consumed by the part handler anymore. But it would not work for binary files.
In another example, I found a reference to a utility provided by Poco, Poco provides Poco::StreamCopier::copyStream() and copyStreamUnbuffered(). Those loop on the stream until the boolean conversion is false. And this works just fine. Including for binary files.
It's a pity such description was not directly given in the Poco documentation. It would be so much easier to find out.
And in fact, it is a pity that they deliver only one PartHandler implementation, the NullPartHandler (which they use internally when no handler is provided). They could deliver a PartHandler that grabs the content in a string (since multiple files are possible, the handler must be an associative container, like a derivative of NameValueCollection) and one that grabs content in temporary files. For another version maybe…