Upload wxImage with wxHTTP Post request - c++

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");

Related

Azure C++ library: "Invalid streambuf object"

I am trying to download a potentially huge Azure block blob, using the C++ Azure client library. It isn't working because I don't know how to initialize a concurrency::streams::streambuf object with a buffer size. My code looks like this:
// Assume blockBlob has been created correctly.
concurrency::streams::istream blobStream = blockBlob.open_read();
// I don't know how to initialize this streambuf:
concurrency::streams::streambuf<uint8_t> dlStreamBuf;
size_t nBytesReturned = 0, nBytesToRead = 65536;
do {
// This gets the exception "Invalid streambuf object":
concurrency::task<size_t> returnedTask = blobStream.read(dlStreamBuf, nBytesToRead);
nBytesReturned = returnedTask.get();
bytesSoFar += nBytesReturned;
// Process the data in dlStreamBuf here...
} while(nBytesReturned > 0);
blobStream.close();
Note that the above streambuf is not to be confused with a standard C++ streambuf.
Can anyone advise me on how to properly construct and initialize a concurrency::streams::streambuf?
Thanks.
streambuf seems to be a template class. Try this instead:
concurrency::streams::container_buffer<std::vector<uint8_t>> output_buffer;
size_t nBytesReturned = 0, nBytesToRead = 65536;
do {
// This gets the exception "Invalid streambuf object":
concurrency::task<size_t> returnedTask = stream.read(output_buffer, nBytesToRead);
nBytesReturned = returnedTask.get();
bytesSoFar += nBytesReturned;
// Process the data in dlStreamBuf here...
} while (nBytesReturned > 0);
stream.close();
Sample code is here: https://github.com/Azure/azure-storage-cpp/blob/76cb553249ede1e6f05456d936c9a36753cc1597/Microsoft.WindowsAzure.Storage/tests/blob_streams_test.cpp#L192
I haven't used the stream methods for C++, but there are two ways mentioned in the C++ documentation about downloading to files or to steams here
The download_to_stream method ex:
// Retrieve storage account from connection string.
azure::storage::cloud_storage_account storage_account = azure::storage::cloud_storage_account::parse(storage_connection_string);
// Create the blob client.
azure::storage::cloud_blob_client blob_client = storage_account.create_cloud_blob_client();
// Retrieve a reference to a previously created container.
azure::storage::cloud_blob_container container = blob_client.get_container_reference(U("my-sample-container"));
// Retrieve reference to a blob named "my-blob-1".
azure::storage::cloud_block_blob blockBlob = container.get_block_blob_reference(U("my-blob-1"));
// Save blob contents to a file.
concurrency::streams::container_buffer<std::vector<uint8_t>> buffer;
concurrency::streams::ostream output_stream(buffer);
blockBlob.download_to_stream(output_stream);
std::ofstream outfile("DownloadBlobFile.txt", std::ofstream::binary);
std::vector<unsigned char>& data = buffer.collection();
outfile.write((char *)&data[0], buffer.size());
outfile.close();
Alternative, using download_to_file:
// Retrieve storage account from connection string.
azure::storage::cloud_storage_account storage_account = azure::storage::cloud_storage_account::parse(storage_connection_string);
// Create the blob client.
azure::storage::cloud_blob_client blob_client = storage_account.create_cloud_blob_client();
// Retrieve a reference to a previously created container.
azure::storage::cloud_blob_container container = blob_client.get_container_reference(U("my-sample-container"));
// Retrieve reference to a blob named "my-blob-2".
azure::storage::cloud_block_blob text_blob = container.get_block_blob_reference(U("my-blob-2"));
// Download the contents of a blog as a text string.
utility::string_t text = text_blob.download_text();

How to get data from Photon eventContent dictionary

We are receiving this callback using ExitGames Photon Realtime engine when an event is fired
customEventAction(int playerNr,
nByte eventCode,
const ExitGames::Common::Object& eventContent)
If the object is a string we use this code to extract it
ExitGames::Common::JString str =
ExitGames::Common::ValueObject<ExitGames::Common::JString>(eventContent).getDataCopy();
However, the object being sent is a dictionary. It's being sent from the server using BroadcastEvent.
How do we get data out of it ?
We've tried this, but it doesn't make any sense:
ExitGames::Common::Dictionary<byte,ExitGames::Common::Object> pdic
= ExitGames::Common::ValueObject<ExitGames::Common::Dictionary<byte,ExitGames::Common::Object>>(eventContent).getDataCopy();
I've found code to get the data from a hashtable, but that doesn't work either.
thanks
Shaun
ExitGames::Common::Dictionary<nByte, ExitGames::Common::Object> dic = ExitGames::Common::ValueObject<ExitGames::Common::Dictionary<nByte, ExitGames::Common::Object> >(eventContent).getDataCopy();
is absolutely correct and works for me.
The cause of your problem must be inside another line.
When you replace the implementations of sendEvent() and customEventAction() in demo_loadBalancing inside one of the Photon C++ client SDKs with the following snippets, then that demo successfully sends and receives a Dictionary:
send:
void NetworkLogic::sendEvent(void)
{
ExitGames::Common::ValueObject<ExitGames::Common::JString> obj(L"test");
ExitGames::Common::Dictionary<nByte, ExitGames::Common::Object> dic;
dic.put(1, obj);
mLoadBalancingClient.opRaiseEvent(false, dic, 0);
}
receive:
void NetworkLogic::customEventAction(int /*playerNr*/, nByte /*eventCode*/, const ExitGames::Common::Object& eventContent)
{
EGLOG(ExitGames::Common::DebugLevel::ALL, L"");
ExitGames::Common::Dictionary<nByte, ExitGames::Common::Object> dic = ExitGames::Common::ValueObject<ExitGames::Common::Dictionary<nByte, ExitGames::Common::Object> >(eventContent).getDataCopy();
const ExitGames::Common::Object* pObj = dic.getValue(1);
ExitGames::Common::JString str = ExitGames::Common::ValueObject<ExitGames::Common::JString>(pObj).getDataCopy();
mpOutputListener->write(L"received the following string as Dictionary value: " + str);
}
This gives me the following line of output on the receiving client:
received the following string as Dictionary value: test

Some Problems of Indy 10 IdHTTP Implementation

In regard to Indy 10 of IdHTTP, many things have been running perfectly, but there are a few things that don't work so well here. That is why, once again, I need your help.
Download button has been running perfectly. I'm using the following code :
void __fastcall TForm1::DownloadClick(TObject *Sender)
{
MyFile = SaveDialog->FileName;
TFileStream* Fist = new TFileStream(MyFile, fmCreate | fmShareDenyNone);
Download->Enabled = false;
Urlz = Edit1->Text;
Url->Caption = Urlz;
try
{
IdHTTP->Get(Edit1->Text, Fist);
IdHTTP->Connected();
IdHTTP->Response->ResponseCode = 200;
IdHTTP->ReadTimeout = 70000;
IdHTTP->ConnectTimeout = 70000;
IdHTTP->ReuseSocket;
Fist->Position = 0;
}
__finally
{
delete Fist;
Form1->Updated();
}
}
However, a "Cancel Resume" button is still can't resume interrupted downloads. Meant, it is always sending back the entire file every time I call Get() though I've used IdHTTP->Request->Ranges property.
I use the following code:
void __fastcall TForm1::CancelResumeClick(TObject *Sender)
{
MyFile = SaveDialog->FileName;;
TFileStream* TFist = new TFileStream(MyFile, fmCreate | fmShareDenyNone);
if (IdHTTP->Connected() == true)
{
IdHTTP->Disconnect();
CancelResume->Caption = "RESUME";
IdHTTP->Response->AcceptRanges = "Bytes";
}
else
{
try {
CancelResume->Caption = "CANCEL";
// IdHTTP->Request->Ranges == "0-100";
// IdHTTP->Request->Range = Format("bytes=%d-",ARRAYOFCONST((TFist->Position)));
IdHTTP->Request->Ranges->Add()->StartPos = TFist->Position;
IdHTTP->Get(Edit1->Text, TFist);
IdHTTP->Request->Referer = Edit1->Text;
IdHTTP->ConnectTimeout = 70000;
IdHTTP->ReadTimeout = 70000;
}
__finally {
delete TFist;
}
}
Meanwhile, by using the FormatBytes function, found here, has been able to shows only the size of download files. But still unable to determine the speed of download or transfer speed.
I'm using the following code:
void __fastcall TForm1::IdHTTPWork(TObject *ASender, TWorkMode AWorkMode, __int64 AWorkCount)
{
__int64 Romeo = 0;
Romeo = IdHTTP->Response->ContentStream->Position;
// Romeo = AWorkCount;
Download->Caption = FormatBytes(Romeo) + " (" + IntToStr(Romeo) + " Bytes)";
ForSpeed->Caption = FormatBytes(Romeo);
ProgressBar->Position = AWorkCount;
ProgressBar->Update();
Form1->Updated();
}
Please advise and give an example. Any help would sure be appreciated!
In your DownloadClick() method:
Calling Connected() is useless, since you don't do anything with the result. Nor is there any guarantee that the connection will remain connected, as the server could send a Connection: close response header. I don't see anything in your code that is asking for HTTP keep-alives. Let TIdHTTP manage the connection for you.
You are forcing the Response->ResponseCode to 200. Don't do that. Respect the response code that the server actually sent. The fact that no exception was raised means the response was successful whether it is 200 or 206.
You are reading the ReuseSocket property value and ignoring it.
There is no need to reset the Fist->Position property to 0 before closing the file.
Now, with that said, your CancelResumeClick() method has many issues.
You are using the fmCreate flag when opening the file. If the file already exists, you will overwrite it from scratch, thus TFist->Position will ALWAYS be 0. Use fmOpenReadWrite instead so an existing file will open as-is. And then you have to seek to the end of the file to provide the correct Position to the Ranges header.
You are relying on the socket's Connected() state to make decisions. DO NOT do that. The connection may be gone after the previous response, or may have timed out and been closed before the new request is made. The file can still be resumed either way. HTTP is stateless. It does not matter if the socket remains open between requests, or is closed in between. Every request is self-contained. Use information provided in the previous response to govern the next request. Not the socket state.
You are modifying the value of the Response->AcceptRanges property, instead of using the value provided by the previous response. The server tells you if the file supports resuming, so you have to remember that value, or query it before then attempting to resumed download.
When you actually call Get(), the server may or may not respect the requested Range, depending on whether the requested file supports byte ranges or not. If the server responds with a response code of 206, the requested range is accepted, and the server sends ONLY the requested bytes, so you need to APPEND them to your existing file. However, if the server response with a response code of 200, the server is sending the entire file from scratch, so you need to REPLACE your existing file with the new bytes. You are not taking that into account.
In your IdHTTPWork() method, in order to calculate the download/transfer speed, you have to keep track of how many bytes are actually being transferred in between each event firing. When the event is fired, save the current AWorkCount and tick count, and then the next time the event is fired, you can compare the new AWorkCount and current ticks to know how much time has elapsed and how many bytes were transferred. From those value, you can calculate the speed, and even the estimated time remaining.
As for your progress bar, you can't use AWorkCount alone to calculate a new position. That only works if you set the progress bar's Max to AWorkCountMax in the OnWorkBegin event, and that value is not always know before a download begins. You need to take into account the size of the file being downloaded, whether it is being downloaded fresh or being resumed, how many bytes are being requested during a resume, etc. So there is lot more work involved in displaying a progress bar for a HTTP download.
Now, to answer your two questions:
How to retrieve and save the download file to a disk by using its original name?
It is provided by the server in the filename parameter of the Content-Disposition header, and/or in the name parameter of the Content-Type header. If neither value is provided by the server, you can use the filename that is in the URL you are requesting. TIdHTTP has a URL property that provides the parsed version of the last requested URL.
However, since you are creating the file locally before sending your download request, you will have to create a local file using a temp filename, and then rename the local file after the download is complete. Otherwise, use TIdHTTP.Head() to determine the real filename (you can also use it to determine if resuming is supported) before creating the local file with that filename, then use TIdHTTP.Get() to download to that local file. Otherwise, download the file to memory using TMemoryStream instead of TFileStream, and then save with the desired filename when complete.
when I click http://get.videolan.org/vlc/2.2.1/win32/vlc-2.2.1-win32.exe then the server will process requests to its actual url. http://mirror.vodien.com/videolan/vlc/2.2.1/win32/vlc-2.2.1-win32.exe. The problem is that IdHTTP will not automatically grab through it.
That is because VideoLan is not using an HTTP redirect to send clients to the real URL (TIdHTTP supports HTTP redirects). VideoLan is using an HTML redirect instead (TIdHTTP does not support HTML redirects). When a webbrowser downloads the first URL, a 5 second countdown timer is displayed before the real download then begins. As such, you will have to manually detect that the server is sending you an HTML page instead of the real file (look at the TIdHTTP.Response.ContentType property for that), parse the HTML to determine the real URL, and then download it. This also means that you cannot download the first URL directly into your target local file, otherwise you will corrupt it, especially during a resume. You have to cache the server's response first, either to a temp file or to memory, so you can analyze it before deciding how to act on it. It also means you have to remember the real URL for resuming, you cannot resume the download using the original countdown URL.
Try something more like the following instead. It does not take into account for everything mentioned above (particularly speed/progress tracking, HTML redirects, etc), but should get you a little closer:
void __fastcall TForm1::DownloadClick(TObject *Sender)
{
Urlz = Edit1->Text;
Url->Caption = Urlz;
IdHTTP->Head(Urlz);
String FileName = IdHTTP->Response->RawHeaders->Params["Content-Disposition"]["filename"];
if (FileName.IsEmpty())
{
FileName = IdHTTP->Response->RawHeaders->Params["Content-Type"]["name"];
if (FileName.IsEmpty())
FileName = IdHTTP->URL->Document;
}
SaveDialog->FileName = FileName;
if (!SaveDialog->Execute()) return;
MyFile = SaveDialog->FileName;
TFileStream* Fist = new TFileStream(MyFile, fmCreate | fmShareDenyWrite);
try
{
try
{
Download->Enabled = false;
Resume->Enabled = false;
IdHTTP->Request->Clear();
//...
IdHTTP->ReadTimeout = 70000;
IdHTTP->ConnectTimeout = 70000;
IdHTTP->Get(Urlz, Fist);
}
__finally
{
delete Fist;
Download->Enabled = true;
Updated();
}
}
catch (const EIdHTTPProtocolException &)
{
DeleteFile(MyFile);
throw;
}
}
void __fastcall TForm1::ResumeClick(TObject *Sender)
{
TFileStream* Fist = new TFileStream(MyFile, fmOpenReadWrite | fmShareDenyWrite);
try
{
Download->Enabled = false;
Resume->Enabled = false;
IdHTTP->Request->Clear();
//...
Fist->Seek(0, soEnd);
IdHTTP->Request->Ranges->Add()->StartPos = Fist->Position;
IdHTTP->Request->Referer = Edit1->Text;
IdHTTP->ConnectTimeout = 70000;
IdHTTP->ReadTimeout = 70000;
IdHTTP->Get(Urlz, Fist);
}
__finally
{
delete Fist;
Download->Enabled = true;
Updated();
}
}
void __fastcall TForm1::IdHTTPHeadersAvailable(TObject*Sender, TIdHeaderList *AHeaders, bool &VContinue)
{
Resume->Enabled = ( ((IdHTTP->Response->ResponseCode == 200) || (IdHTTP->Response->ResponseCode == 206)) && TextIsSame(AHeaders->Values["Accept-Ranges"], "bytes") );
if ((IdHTTP->Response->ContentStream) && (IdHTTP->Request->Ranges->Count > 0) && (IdHTTP->Response->ResponseCode == 200))
IdHTTP->Response->ContentStream->Size = 0;
}
#Romeo:
Also, you can try a following function to determine the real download filename.
I've translated this to C++ based on the RRUZ'function. So far so good, I'm using it on my simple IdHTTP download program, too.
But, this translation result is of course still need value improvement input from Remy Lebeau, RRUZ, or any other master here.
String __fastcall GetRemoteFileName(const String URI)
{
String result;
try
{
TIdHTTP* HTTP = new TIdHTTP(NULL);
try
{
HTTP->Head(URI);
result = HTTP->Response->RawHeaders->Params["Content-Disposition"]["filename"];
if (result.IsEmpty())
{
result = HTTP->Response->RawHeaders->Params["Content-Type"]["name"];
if (result.IsEmpty())
result = HTTP->URL->Document;
}
}
__finally
{
delete HTTP;
}
}
catch(const Exception &ex)
{
ShowMessage(const_cast<Exception&>(ex).ToString());
}
return result;
}

How to create new GMimeMessage from string?

In my project i use libgmime for MIME types. I'm trying to create new GMimeMessage using std::string as a body.
According to docs it can be done using GMimeStream and GMimeDataWrapper for preparing data, and then creating GMimePart from this data to be set as MIME part of new message.
The code:
std::string body = "some test data";
GMimeMessage* message = g_mime_message_new(FALSE);
//set header
g_mime_object_set_header((GMimeObject *) message, name.c_str()), value.c_str();
//create stream and write data into it.
GMimeStream* stream;
g_mime_stream_construct(stream, 0, body.length());
g_mime_stream_write_string(stream, body.c_str());
GMimeDataWrapper* wrapper = g_mime_data_wrapper_new_with_stream(stream, GMIME_CONTENT_ENCODING_DEFAULT);
//create GMimePart to be set as mime part of GMimeMessage
GMimePart* mime_part = g_mime_part_new();
g_mime_part_set_content_object(mime_part, wrapper);
g_mime_message_set_mime_part(message, (GMimeObject *) mime_part);
When i try to create message in this way, i get segfault here:
g_mime_stream_write_string(stream, body.c_str());
Maybe i'm using wrong method of message creation...
What's the right way it can be done?
You have bad initialization GMimeStream *stream. Need:
GMimeStream *stream;
/* initialize GMime */
g_mime_init (0);
/* create a stream around stdout */
stream = g_mime_stream_mem_new_with_buffer(body_part.c_str(), body_part.length());
See doc: http://spruce.sourceforge.net/gmime/tutorial/x49.html
And sample: http://fossies.org/linux/gmime/examples/basic-example.c

Sending an image using Poco to a webserver

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.