Sending an image using Poco to a webserver - c++

I'm trying to upload a picture to appengine using POCO::Net::HTMLForm.
Please see the code below. The problem I have is that the imagedata is not send. The token and the character name (its for an open source action rpg) is transmited without any problem. Any idea what I may do wrong?
Thanks in advance :)
c++ code:
// prepare session
Poco::URI uri(backend_url + "/api/update_character_avatar");
Poco::Net::HTTPClientSession session(uri.getHost(), uri.getPort());
// prepare path
std::string path(uri.getPathAndQuery());
if (path.empty()) path = "/";
Poco::Net::HTTPRequest req(Poco::Net::HTTPRequest::HTTP_POST, path);
Poco::Net::HTMLForm form;
form.add("token", sw_token);
form.add("charname", sw_charname);
Poco::Buffer<char> imgBuffer(mImgPtr->size());
mImgPtr->read(imgBuffer.begin(), imgBuffer.end()-imgBuffer.begin());
std::string s(imgBuffer.begin(), mImgPtr->size());
std::ostringstream out;
Poco::Base64Encoder b64enc(out);
b64enc.write(imgBuffer.begin(), imgBuffer.end()-imgBuffer.begin());
b64enc.close();
Poco::Net::StringPartSource *prtsrc = new Poco::Net::StringPartSource(out.str());
form.addPart("imagedata", prtsrc);
form.prepareSubmit(req);
std::ostream& send = session.sendRequest(req);
form.write(send);
// get response
Poco::Net::HTTPResponse res;
// print response
std::istream &is = session.receiveResponse(res);
std::ostringstream stream;
Poco::StreamCopier::copyStream(is, stream);
std::cout << stream.str() << std::endl;
Python handler for appengine:
class UpdateCharacterAvatarHandler(webapp2.RequestHandler):
def post(self):
token = self.request.get("token")
charname = self.request.get("charname")
imagedata = self.request.get("imagedata")
logging.error(self.request.body)
self.response.write("test")

Sorry I couldn't understand what exactly you are trying to do in this code. There are lot of useless steps you have taken to upload a file as it can be done simply by using HTMLForm class. Here are the minimum code required to do so -
HTTPRequest request(HTTPRequest::HTTP_POST, "/fileupload/upload_file.php", HTTPMessage::HTTP_1_1);
HTMLForm form;
form.setEncoding(HTMLForm::ENCODING_MULTIPART);
form.addPart("file", new FilePartSource("/home/abc/Pictures/sample.png"));
form.prepareSubmit(request);
HTTPClientSession *httpSession = new HTTPClientSession("localhost");
httpSession->setTimeout(Poco::Timespan(20, 0));
form.write(httpSession->sendRequest(request));
Poco::Net::HTTPResponse res;
std::istream &is = httpSession->receiveResponse(res);
Poco::StreamCopier::copyStream(is, std::cout);
The corresponding upload server is using standard PHP code for uploading HTML form files.

I would expect imagedata to appear as a post field rather than get, so I would try     imagedata = self.request.post("imagedata"). Alternatively try posting it to a server which will allow you to set breakpoints and inspect the content of the request.

Related

Write a cpprestsdk json value object into a file

I receive a JSON response body as part of a REST request. I would like to write the body of the JSON into a file. A quick way to do this would be to use the web::http::response object itself..
pplx::task<void> requestTask = fstream::open_ostream(U("testResults.json")).then([=](ostream outFile)
{
*fileStream = outFile;
//some work
})
.then([=](http_response response)
{
printf("Received response status code:%u\n", response.status_code());
// Write response body into the file.
return response.body().read_to_end(fileStream->streambuf());
})
.then([=](size_t s)
{
return fileStream->close();
});
However, after receiving the response body, I extract the JSON from it to calculate some values, after which, as documented, the json cannot be extracted or used again from the response body, so i have to use the web::json::value object.
http::json::value someValue = response.extract_json().get();
I'd like to write this json::value object, someValue, to a JSON file, but not sure how to get it done. Writing to an ostream object with serialize.c_cstr() on someValue gives weird output.
ostream o("someFile.json");
o << setw(4) << someValue.serialize().c_str() << std<<endl;
If your system typdef std::string as a wide string, then outputting via ostream would lead to weird output.
web::json::value::serialize() returns a utility::string_t
https://microsoft.github.io/cpprestsdk/classweb_1_1json_1_1value.html#a5bbd4bca7b13cd912ffe76af825b0c6c
utility::string_t is typedef as a std::string
https://microsoft.github.io/cpprestsdk/namespaceutility.html#typedef-members
So something like this would work:
std::filesystem::path filePath(L"c:\\someFile.json"); // Assuming wide chars here. Could be U instead of L depending on your setup
std::wofstream outputFile(filePath);
outputFile << someJSONValue.serialize().c_str();
outputFile.close();

Minio: Why is my PUT request failing, even though GET is successful?

I am using Minio to emulate S3 and test my code locally. My code is written using the AWS SDK for C++.
What I would like to do (for testing purposes) is to get an object from Minio, store it and then send the same object back to Minio using a PUT request. The PUT request fails with the error Unable to connect to endpoint. I am however able to use curl to PUT objects to Minio.
This is how I set up my S3Client (I added some likes to explain why I did stuff):
auto credentialsProvider = Aws::MakeShared<Aws::Auth::EnvironmentAWSCredentialsProvider>("someTag");
Aws::Client::ClientConfiguration config;
config.endpointOverride = Aws::String("172.17.0.2:9000");
config.scheme = Aws::Http::Scheme::HTTP;
// diable ssl https://github.com/aws/aws-sdk-cpp/issues/284
config.verifySSL = false;
// set region to default https://github.com/awslabs/amazon-kinesis-producer/issues/66
config.region = "us-east-1";
// disable virtual adress and signing https://stackoverflow.com/questions/47105289/how-to-override-endpoint-in-aws-sdk-cpp-to-connect-to-minio-server-at-localhost
Aws::S3::S3Client client(credentialsProvider, config, Aws::Client::AWSAuthV4Signer::PayloadSigningPolicy::Never, false);
This is how both my GET and my PUT request look like. GET works, PUT does not:
// declare request
Aws::S3::Model::GetObjectRequest get_obj_req;
get_obj_req.WithBucket("someBucket").WithKey("someKey");
// Get works fine
auto get_object_outcome = client.GetObject(get_obj_req);
if (!get_object_outcome.IsSuccess()){
// fail does not happen
}
// write file from Minio to local file (seems to work fine)
int size = 1024;
char buffer[size];
auto &retrieved_file = get_object_outcome.GetResultWithOwnership().GetBody().read(buffer, size);
std::ofstream out("someFile");
out << std::string(buffer);
out.close();
// try to PUT the stored file again
Aws::S3::Model::PutObjectRequest put_obj_req;
const std::shared_ptr<Aws::IOStream> input_data =
Aws::MakeShared<Aws::FStream>("tag", "someFile", std::ios_base::in | std::ios_base::binary);
put_obj_req.SetBody(input_data);
put_obj_req.WithBucket("someBucket").WithKey("someKey");
put_obj_req.SetContentLength(size);
put_obj_req.SetContentType("application/octet-stream");
// PUT request
auto resp = client.PutObject(put_obj_req);
if (!resp.IsSuccess()){
// fails here
}
As I already mentioned, I am able to PUT objects to Minio, using curl. You can have a look in this Gist.
Sidenote: I am using Minio inside a Docker container.
EDIT: I believe this might be a problem with the data that I want to PUT. If the data has the e.g. Content-Type application/octet-stream I run into an error, but I do not run into this error when using txt-files. My current code looks like this and I assume that the streaming breaks if I want to stream anything but chers. Can you confirm?
Aws::String content_tye = get_object_outcome.GetResult().GetContentType();
Aws::IOStream &retrieved_file = get_object_outcome.GetResultWithOwnership().GetBody();
retrieved_file.seekg(0, retrieved_file.end);
int retrieved_file_size = retrieved_file.tellg();
retrieved_file.seekg(0, retrieved_file.beg);
char *buffer = new char[retrieved_file_size];
retrieved_file.read(buffer, retrieved_file_size);
AWS_LOGSTREAM_INFO(TAG, "Retrieved file of size: " + Aws::Utils::StringUtils::to_string(retrieved_file_size));
Aws::StringStream stream(Aws::String(buffer));
const std::shared_ptr<Aws::IOStream> input_data =
Aws::MakeShared<Aws::StringStream>(TAG, Aws::String(buffer));

How can I issue a POST request that contains a basic authentication header, and a JSON body?

I am trying to use the CPPRESTSDK (a.k.a. Casablanca) to POST data to a RESTful server. To do this, I create a request, and assign a header:
// create request, and add header information
web::http::http_request req(methods::POST);
req.headers().add(header_names::authorization, authStr); // authStr is base64 representation of username & password
req.headers().add(header_names::content_type, http::details::mime_types::application_json);
Next, I make a web::json::value object that contains all the key-value pairs:
web::json::value obj = json::value::object();
obj[U("Key1")] = web::json::value::string(U("Val1")];
obj[U("Key2")] = web::json::value::string(U("Val2")];
obj[U("Key3")] = web::json::value::string(U("Val3")];
I then store this object in the request's body by calling:
req.set_body(obj);
Finally, I send the request to the server using an http_client:
// create http client
web::http::client::http_client client(addr); // addr is wstring
return client.request(req).then([](http_response response) {
return response;
});
The problem is that this doesn't do anything. If I place a breakpoint on this line, I get information about "400 Bad Request." I would assume that the request's body is somehow malformed, but it could also be that I am missing some information in the header. This error does not happen when I issue a GET request on the same URL, so it is definitely a problem with POSTs specifically. What do you think?
Here is a working example:
// create a new channel
pplx::task<web::http::http_response> postChannel(http_client client, std::wstring authStr, std::wstring cDesc, std::wstring cName, std::string cDiagCap, int cNormFloat, int cWriteDuty,
int cWriteMeth, std::string cItemPersist, std::wstring cItemPersistDat) {
// create request
http_request req(methods::POST);
req.headers().add(header_names::authorization, authStr);
std::wstring url = L"/config/v1/project/channels";
req.set_request_uri(url);
json::value obj = json::value::object();
obj[U("common.ALLTYPES_DESCRIPTION")] = json::value::string(cDesc);
obj[U("common.ALLTYPES_NAME")] = json::value::string(cName);
obj[U("servermain.CHANNEL_DIAGNOSTICS_CAPTURE")] = json::value(cDiagCap == "true" || cDiagCap == "t");
obj[U("servermain.CHANNEL_NON_NORMALIZED_FLOATING_POINT_HANDLING")] = json::value(cNormFloat);
obj[U("servermain.CHANNEL_WRITE_OPTIMIZATIONS_DUTY_CYCLE")] = json::value(cWriteDuty);
obj[U("servermain.CHANNEL_WRITE_OPTIMIZATIONS_METHOD")] = json::value(cWriteMeth);
obj[U("servermain.MULTIPLE_TYPES_DEVICE_DRIVER")] = json::value::string(U("Simulator")); // right now, Simulator channels are the only option
obj[U("simulator.CHANNEL_ITEM_PERSISTENCE")] = json::value(cItemPersist == "true" || cItemPersist == "t");
obj[U("simulator.CHANNEL_ITEM_PERSISTENCE_DATA_FILE")] = json::value::string(cItemPersistDat);
req.set_body(obj);
return client.request(req).then([](http_response response) {
return response;
});
}

Upload wxImage with wxHTTP Post request

How can I upload an image stored in wxImage or wxBitmap with a wxHTTP POST request?
I know I can do it somehow with:
wxImage::SaveFile (wxOutputStream &stream, wxBitmapType type) and
wxHTTP::SetPostBuffer (const wxString &contentType, const wxMemoryBuffer &data)
But I just started with cpp an wx.
You are almost there. The missing piece is some class implementing the wxOutputStream interface, whose content you could then send using the SetPostBuffer() method.
You can see all provided implementations of wxOutputStream here. It seems that you are looking for wxMemoryOutputStream.
The full code part therefore would be something like this:
wxMemoryOutputStream stream;
if (!myImage.SaveFile(stream, wxBITMAP_TYPE_PNG))
;// TODO: Handle error
SetPostBuffer("image/png", *stream.GetOutputStreamBuffer());
This is how I did it. A bit more verbose, but minus the error checking...
The member vars are wxURLs.
// ok now read the data into a buffer again
wxMemoryBuffer buf;
{
// don't have charBuf around for too long
wxFile f(someFileName);
char charBuf[f.Length()];
f.Read(charBuf, f.Length());
buf.AppendData(charBuf, f.Length());
}
// upload!
wxHTTP post;
post.SetPostBuffer("application/gzip", buf);
post.Connect(mDestUrl.GetServer()):
wxInputStream *iStream = post.GetInputStream(mDestUrl.GetPath() + "?" + mDestUrl.GetQuery());
// put result into stream
wxString res;
wxStringOutputStream oStream(&res);
iStream->Read(oStream);
wxLogDebug(TAG " server replied: " + res);
Note that wxHttp does not do a multipart upload. In PHP,for instance, you can access the data via
$postdata = file_get_contents("php://input");

gsoap c++ android device encoding

8.15,
I can connect my microsoft web service and I can insert record with this service easily.
I get a confirmation code as a response for record insert. But I have a problem with encoding. The response message must like this 1Exa9GwOIO6pP35l4TJ1Bw== but instead of this I get a response like this 4�� u #
When I try this service on a browser I get the expected response as
in 1Exa9GwOIO6pP35l4TJ1Bw==
But when I try it on an android device with gsoap I get a response such as this one 4�� u #
How can I solve this encoding problem?
TheGameSoapProxy service;
_ns1__PlayerRegisterResponse* response = new _ns1__PlayerRegisterResponse();
std::string telNO =m_pEditTel->getText();
std::string telefonIME = "111";
std::string simCardID = "222";
std::string Username = m_pEditName->getText();
std::string takim = Takim.c_str();
_ns1__PlayerRegister* ps = new _ns1__PlayerRegister();
ps->telefonNumarasi = &telNO;
ps->telefonIME = &telefonIME;
ps->simCardID = &simCardID;
ps->Username = &Username;
ps->takim = &takim;
if (service.PlayerRegister(ps, response) == SOAP_OK)
{
string *ptrSonuc = response->PlayerRegisterResult;
CCLog( (char*)ptrSonuc );
}
As per other question here on SO:
add the line below to your typemap.dat file:
xsd__string = | wchar_t* | wchar_t*
And then use wstrings instead of strings.