Cinder-Http Multipart Example


#1

Hey guys,

I’m using Ryan’s Cinder-Http block (using the UpdateBlock branch) for a current project at our studio. I’m trying to get my head around MultiPart Forms but can’t succeed. I’ve seen the source code and the part that is related to Multiparts but can’t figure out some stuff:

My task is to send a couple of jpeg photos along with some string data to an html form as follows

<form method="post" action="postImage.php" enctype='multipart/form-data'>
    <input type='file' name='files[]' multiple="multiple" /><br />
    <input type="number" name="id" placeholder="Machine ID"><br />
    <input type="text" name="fname" placeholder="first name"><br />
    <input type="text" name="lname" placeholder="last name"><br />
    <input type="number" name="phone" placeholder="phone"><br />
    <input type="email" name="email" placeholder="email"><br />
    <input type='submit' value='Submit' name='but_upload'>    
</form>

questions:

1- How to send a couple of jpegs in one part? In this case since the form accepts only one input for all the files (files[]), I think I need to do that. Or am I thinking wrong that the parts and html inputs are related somehow?

2- “Part” has an appendHeader() method. Are headers here equivilant to MIME “Content-Type” or is it something totally different? Is calling this method a must for each part or is it optional? I’m asking this since Multipart already has its header.

3- for each Part I should set the body as a Buffer, right? something like this ?

srand(time(NULL));
http::MultipartFormData formData(toString(rand()));

http::MultipartFormData::Part part;
string fileName ="test.jpg";
part.setBody(Buffer(loadFile(fileName)));
formData.appendPart(std::move(part));

In general I think it’d be great for the block to have an example with multiparts.

Thanks a lot


#2

For future reference and others who might find it hard to grasp like me:
When I looked deeper into the block’s code, I realized that headers should be filled with the info you need for the subparts. That is besides the Content-Type: multipart/form-data; boundary and Content-Length headers which is set by the block for you. The header (which basically is a std::pair<string, string>) works in a way that the block puts a separator ‘:’ between its first and second value. So for instance to add the header Content-Disposition: form-data; name="text1" to your subpart you’d have to write:

http::MultipartFormData::Part part;
part.appendHeader(string("Content-Disposition"), string("form-data; name=\"text1\""));

or for specifying the type of your content, you’d say:

http::MultipartFormData::Part part;
part.appendHeader(string("Content-Type"), string("text/plain; charset=utf-8"));

For the data itself, you’d need to setBody() on the subpart


#3

Also when it comes to a file[] input in your form you can just send separate files to the same input name or you at the same time can specify the order of the files by appending the necessary headers. So for instance for the form I mentioned in my first post, I ended up with this code:

auto url = make_shared<http::Url>(mUrlLink);
auto request = std::make_shared<http::Request>(http::RequestMethod::POST, url);

// making MultipartFormData
srand(time(NULL));
http::MultipartFormData formData(string("xxx") + toString(rand()) + string("xxx"));
console() << formData.delimiter << endl;
// images
for (int i = 0; i < SESSION_PHOTO_COUNT; i++) {
	http::MultipartFormData::Part part;
	string fileName = mArSession.mPhone + "___" + toString(i) + ".jpg";
	console() << mApp->getCaptureFolder() << "     " << fileName << "   " << endl;
	part.appendHeader(string("Content-Disposition"), string("form-data; name=\"files[]\"; filename=\"") + fileName + "\"");
	part.appendHeader(string("Content-Type"), string("image/jpeg"));
	part.setBody(Buffer(loadFile(mApp->getCaptureFolder() + fileName)));
	formData.appendPart(std::move(part));
}

{ // machine id
	http::MultipartFormData::Part part;
	part.appendHeader(string("Content-Disposition"), string("form-data; name=\"id\""));
	part.appendHeader(string("Content-Type"), string("text/plain; charset=utf-8"));
	part.setBody(toString(mApp->mSettings->mId));
	formData.appendPart(std::move(part));
}

{ // user name
	http::MultipartFormData::Part part;
	part.appendHeader(string("Content-Disposition"), string("form-data; name=\"name\""));
	part.appendHeader(string("Content-Type"), string("text/plain; charset=utf-8"));
	part.setBody(mArSession.mName);
	formData.appendPart(std::move(part));
}

{ // user phone
	http::MultipartFormData::Part part;
	part.appendHeader(string("Content-Disposition"), string("form-data; name=\"phone\""));
	part.appendHeader(string("Content-Type"), string("text/plain; charset=utf-8"));
	part.setBody(mArSession.mPhone);
	formData.appendPart(std::move(part));
}

{ // user email
	http::MultipartFormData::Part part;
	part.appendHeader(string("Content-Disposition"), string("form-data; name=\"email\""));
	part.appendHeader(string("Content-Type"), string("text/plain; charset=utf-8"));
	part.setBody(mArSession.mEmail);
	formData.appendPart(std::move(part));
}



request->appendHeader(http::Connection(http::Connection::Type::CLOSE));
request->appendHeader(http::Accept());

request->appendHeader(http::Content(formData));