Hi,
I want to have my piece of software check for available updates on my server, download and install the latest one. Currently i am using wxURL to make it happen and it works but sooner or later the http will change to https and that means the wxURL will stop functioning.
I already use libcurl in my projects for communication in my https server but i need to see some code examples that will allow me to download a file using libcurl and wxThread. Is there anything available, or even better some guides? I tried to follow the examples at the libcurl website but they are all for C language while i am using C++ with classes and all that.
Thank you in advance
Download file with CURL and wxThread with progressbar
Re: Download file with CURL and wxThread with progressbar
Hi,
You just create cURL wrapper and use it in your code.
Just like before - you said you already using cURL...
Thank you.
You just create cURL wrapper and use it in your code.
Just like before - you said you already using cURL...
Thank you.
-
- Super wx Problem Solver
- Posts: 380
- Joined: Tue Jun 20, 2006 6:47 pm
- Contact:
Re: Download file with CURL and wxThread with progressbar
Here's some old code I had lying around that for downloading with CURL in a secondary thread. I added the progress callbacks and they seem to work.
This only downloads to memory and uses a std::vector for the memory buffer, so it can only be used for small files. This code does nothing but delete the downloaded contents, but presumably real code would save it to a file or use it in some other way before deleting it from memory. I'm not sure I'd do things the same way if I was rewriting this now, so please take it as an example, but not necessarily a good example.
Also, the options set in CurlCtrl::CurlCtrl are based on using the windows built in cert store for https. I think slightly different options would be needed for linux and mac, but I'm not sure what changes will be needed.
Code: Select all
// For compilers that support precompilation, includes "wx/wx.h".
#include "wx/wxprec.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
// for all others, include the necessary headers (this file is usually all you
// need because it includes almost all "standard" wxWidgets headers)
#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif
// wxWidgets headers
#include <wx/thread.h>
#include <wx/msgqueue.h>
// CURL headears
#include <curl/curl.h>
wxDEFINE_EVENT(wxEVT_MYTHREAD_DOWNLOAD_COMPLETE, wxThreadEvent);
wxDEFINE_EVENT(wxEVT_MYTHREAD_DOWNLOAD_PROGRESS, wxThreadEvent);
class Msg
{
public:
enum Message
{
Fetch,
Exit,
MessageLast
};
Msg(){Init();}
Msg(Message m){Init(m);}
Msg(const wxString& s){Init(Fetch,s);}
Message GetMsg() const{return m_message;}
wxString GetFileName() const{return m_file;}
private:
void Init(Message m = MessageLast, const wxString& s = wxString())
{
m_message = m;
m_file=s;
}
Message m_message;
wxString m_file;
};
////////////////////CurlWriteBuffer////////////////////////
class CurlWriteBuffer
{
public:
CurlWriteBuffer():m_writeFailed(false){}
void Reset(){m_data.clear(); m_writeFailed=false;}
bool Write(void*,size_t);
bool WriteFailed() const {return m_writeFailed;}
char* GerBuffer(){return (m_data.empty() ? NULL : &(m_data[0]) );}
size_t GetSize()const {return m_data.size();}
void Reserve(size_t s){m_data.reserve(s);}
size_t GetCapacity() const { return m_data.capacity();}
private:
std::vector<char> m_data;
bool m_writeFailed;
};
bool CurlWriteBuffer::Write(void* contents,size_t sz)
{
try
{
//method for copying the buffer to the vector based on:
//http://stackoverflow.com/questions/4758257/how-to-copy-a-range-of-data-from-char-array-into-a-vector
char* charcontents = reinterpret_cast<char*>(contents);
m_data.insert(m_data.end(), charcontents, charcontents+sz);
}
catch( std::bad_alloc& WXUNUSED(ba) )
{
m_writeFailed=true;
return false;
}
return true;
}
class CurlCtrl
{
public:
CurlCtrl(wxEvtHandler*);
~CurlCtrl();
CURLcode Fetch(const char *);
void ClearBuffer();
char* GetBuffer();
char* GetErrors();
size_t GetSize();
void OnProgress(curl_off_t dltotal, curl_off_t dlnow);
size_t OnWrite(void *contents, size_t size, size_t nmemb);
private:
static size_t WriteMemoryCallback(void*, size_t, size_t, void*);
static int ProgressCallback(void*, curl_off_t, curl_off_t, curl_off_t,
curl_off_t);
CURL* m_handle;
CurlWriteBuffer m_buffer;
char* m_errorMsgs;
wxEvtHandler* m_handler;
wxDateTime m_lastUpdate;
};
CurlCtrl::CurlCtrl(wxEvtHandler* handler):m_buffer(),m_handler(handler)
{
m_lastUpdate = wxDateTime::UNow();
curl_global_init(CURL_GLOBAL_ALL);
/* init the curl session */
m_handle = curl_easy_init();
// Setup a buffer for any error messages
m_errorMsgs = new char[CURL_ERROR_SIZE];
curl_easy_setopt(m_handle, CURLOPT_ERRORBUFFER, m_errorMsgs);
/* send all data to this function */
curl_easy_setopt(m_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
curl_easy_setopt(m_handle, CURLOPT_WRITEDATA, this);
// Handle progress updates
curl_easy_setopt(m_handle, CURLOPT_XFERINFODATA, this);
curl_easy_setopt(m_handle, CURLOPT_XFERINFOFUNCTION, ProgressCallback);
curl_easy_setopt(m_handle, CURLOPT_NOPROGRESS, 0L);
/* some servers don't like requests that are made without a user-agent
field, so we provide one */
curl_easy_setopt(m_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0");
curl_easy_setopt(m_handle, CURLOPT_ACCEPT_ENCODING, "gzip,deflate" );
curl_easy_setopt(m_handle, CURLOPT_SSL_VERIFYPEER, 1L);
curl_easy_setopt(m_handle, CURLOPT_SSL_VERIFYHOST, 0L);
curl_easy_setopt(m_handle, CURLOPT_FOLLOWLOCATION, 1 );
}
CurlCtrl::~CurlCtrl()
{
/* cleanup curl stuff */
curl_easy_cleanup(m_handle);
/* we're done with libcurl, so clean it up */
curl_global_cleanup();
delete[] m_errorMsgs;
}
CURLcode CurlCtrl::Fetch(const char * url)
{
CURLcode rv;
m_buffer.Reset();
curl_easy_setopt(m_handle, CURLOPT_URL, url);
/* get it! */
rv = curl_easy_perform(m_handle);
if ( m_buffer.WriteFailed() )
{
return CURLE_OUT_OF_MEMORY;
}
else
{
return rv;
}
}
void CurlCtrl::OnProgress(curl_off_t dltotal, curl_off_t dlnow)
{
wxDateTime curTime = wxDateTime::UNow();
if ( curTime.GetValue() - m_lastUpdate.GetValue() > 50 )
{
wxThreadEvent* ev = new wxThreadEvent(wxEVT_MYTHREAD_DOWNLOAD_PROGRESS);
wxLongLong dlCur(dlnow);
wxLongLong dlTotal(dltotal);
wxLongLong perc = ( dlTotal == 0 ? 0 : (100*dlnow)/dltotal );
wxString s = wxString::Format("%s/%s = %s %%", dlCur.ToString(),
dlTotal.ToString(), perc.ToString());
size_t d = static_cast<size_t>(dlTotal.GetValue());
if ( d > m_buffer.GetCapacity() )
{
m_buffer.Reserve(d);
}
ev->SetExtraLong(perc.ToLong());
ev->SetString(s);
wxQueueEvent(m_handler, ev);
m_lastUpdate = curTime;
}
}
int CurlCtrl::ProgressCallback(void* clientp, curl_off_t dltotal,
curl_off_t dlnow, curl_off_t ultotal,
curl_off_t ulnow)
{
CurlCtrl* curlCtrl = reinterpret_cast<CurlCtrl*>(clientp);
curlCtrl->OnProgress(dltotal, dlnow);
return CURL_PROGRESSFUNC_CONTINUE;
}
size_t CurlCtrl::OnWrite(void *contents, size_t size, size_t nmemb)
{
size_t realsize = size * nmemb;
if( m_buffer.Write(contents,realsize) )
{
return realsize;
}
else
{
return realsize+1;
}
}
size_t CurlCtrl::WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp)
{
CurlCtrl* curlCtrl = reinterpret_cast<CurlCtrl*>(userp);
return curlCtrl->OnWrite(contents, size, nmemb);
}
void CurlCtrl::ClearBuffer()
{
m_buffer.Reset();
}
char* CurlCtrl::GetBuffer()
{
return m_buffer.GerBuffer();
}
size_t CurlCtrl::GetSize()
{
return m_buffer.GetSize();
}
char* CurlCtrl::GetErrors()
{
return m_errorMsgs;
}
class FetchThread : public wxThread
{
public:
FetchThread(wxEvtHandler* handler, wxMessageQueue<Msg>& q,wxCriticalSection&,bool*);
~FetchThread();
protected:
virtual ExitCode Entry();
private:
void ProcessMessage(Msg& m);
void FetchFile();
wxEvtHandler* m_handler;
wxMessageQueue<Msg>& m_queue;
wxCriticalSection& m_critsect;
bool* m_isRunning;
std::queue<wxString> m_filesToFetch;
CurlCtrl m_curl;
bool m_shutDown;
};
FetchThread::FetchThread(wxEvtHandler* handler, wxMessageQueue<Msg>& q, wxCriticalSection& c,bool* d)
:wxThread(wxTHREAD_DETACHED),m_queue(q),m_critsect(c),m_curl(handler)
{
m_handler = handler;
m_isRunning = d;
m_shutDown = false;
}
FetchThread::~FetchThread()
{
{
wxCriticalSectionLocker enter(m_critsect);
// the thread is being destroyed.
*m_isRunning = false;
}
}
void FetchThread::ProcessMessage(Msg& m)
{
if ( m.GetMsg() == Msg::Fetch )
{
m_filesToFetch.push(m.GetFileName());
}
else if ( m.GetMsg() == Msg::Exit )
{
m_shutDown = true;
}
}
void FetchThread::FetchFile()
{
wxString fileToFetch = m_filesToFetch.front();
m_filesToFetch.pop();
CURLcode r = m_curl.Fetch(fileToFetch);
wxThreadEvent* ev = new wxThreadEvent(wxEVT_MYTHREAD_DOWNLOAD_COMPLETE);
void* buffer = NULL;
if ( r == CURLE_OK )
{
size_t sz = m_curl.GetSize();
buffer = malloc(sz);
memcpy(buffer, m_curl.GetBuffer(), sz);
ev->SetInt(sz);
}
ev->SetString(fileToFetch);
ev->SetPayload<void*>(buffer);
wxQueueEvent(m_handler, ev);
}
wxThread::ExitCode FetchThread::Entry()
{
wxMessageQueueError e;
Msg m;
while ( true )
{
// Check if termination was requested
if ( m_shutDown || TestDestroy() )
{
break;
}
// Check if there is a message available immediately.
e = m_queue.ReceiveTimeout(0, m);
if ( e == wxMSGQUEUE_NO_ERROR )
{
ProcessMessage(m);
continue;
}
if ( !m_filesToFetch.empty() )
{
FetchFile();
continue;
}
// Now wait for a message.
e = m_queue.ReceiveTimeout(100, m);
if ( e == wxMSGQUEUE_NO_ERROR )
{
ProcessMessage(m);
}
}
return (wxThread::ExitCode)0; // success
}
class MyFrame: public wxFrame
{
public:
MyFrame();
~MyFrame();
private:
void OnFetch(wxCommandEvent& event);
void OnFetchComplete(wxThreadEvent& event);
void OnFetchProgress(wxThreadEvent& event);
wxMessageQueue<Msg> m_queue;
wxCriticalSection m_pThreadCS;
bool m_threadIsRunning;
wxTextCtrl* m_urlTextCtrl;
wxTextCtrl* m_msgArea;
wxGauge* m_gauge;
};
MyFrame::MyFrame()
:wxFrame(NULL, wxID_ANY, "CURL Fetch frame", wxDefaultPosition,
wxSize(600, 400))
{
wxPanel* mainPanel = new wxPanel(this,wxID_ANY);
wxStaticText* urlPrompt = new wxStaticText(mainPanel, wxID_ANY, "Url:");
m_urlTextCtrl = new wxTextCtrl(mainPanel, wxID_ANY, wxEmptyString);
wxButton* fetchButton = new wxButton(mainPanel, wxID_ANY, "Fetch");
m_gauge = new wxGauge( mainPanel, wxID_ANY, 100 );
m_gauge->SetValue( 0 );
m_msgArea = new wxTextCtrl( mainPanel, wxID_ANY, wxEmptyString,
wxDefaultPosition, wxDefaultSize,
wxTE_DONTWRAP|wxTE_MULTILINE );
wxBoxSizer* topSizer = new wxBoxSizer( wxHORIZONTAL );
topSizer->Add(urlPrompt, wxSizerFlags(0).CenterVertical().Border(wxALL));
topSizer->Add(m_urlTextCtrl, wxSizerFlags(1).CenterVertical().Border(wxTOP|wxBOTTOM|wxRIGHT));
topSizer->Add(fetchButton, wxSizerFlags(0).CenterVertical().Border(wxTOP|wxBOTTOM|wxRIGHT));
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
mainSizer->Add(topSizer,wxSizerFlags(0).Expand());
mainSizer->Add(m_gauge,wxSizerFlags(0).Expand().Border(wxLEFT|wxRIGHT|wxBOTTOM));
mainSizer->Add(m_msgArea,wxSizerFlags(1).Expand().Border(wxLEFT|wxRIGHT|wxBOTTOM));
mainPanel->SetSizer(mainSizer);
Layout();
m_threadIsRunning = true;
{
wxThread* t = new FetchThread(this,m_queue,m_pThreadCS,&m_threadIsRunning);
if ( t->Run() != wxTHREAD_NO_ERROR )
{
m_threadIsRunning = false;
}
}
if ( !m_threadIsRunning )
{
m_msgArea->AppendText("Can't create the thread!\n");
fetchButton->Disable();
m_urlTextCtrl->Disable();
}
Bind(wxEVT_MYTHREAD_DOWNLOAD_COMPLETE, &MyFrame::OnFetchComplete, this);
Bind(wxEVT_MYTHREAD_DOWNLOAD_PROGRESS, &MyFrame::OnFetchProgress, this);
fetchButton->Bind(wxEVT_BUTTON, &MyFrame::OnFetch, this);
}
MyFrame::~MyFrame()
{
Msg m(Msg::Exit);
m_queue.Post(m);
while (1)
{
{ // was the ~MyThread() function executed?
wxCriticalSectionLocker enter(m_pThreadCS);
if ( !m_threadIsRunning )
{
break;
}
}
// wait for thread completion
wxThread::This()->Sleep(1);
}
}
void MyFrame::OnFetch(wxCommandEvent &event)
{
if ( m_threadIsRunning )
{
wxString url = m_urlTextCtrl->GetValue();
Msg m(url);
wxString s = wxString::Format("Fetching %s.\n",url);
m_msgArea->AppendText(s);
m_queue.Post(m);
}
else
{
m_msgArea->AppendText("Fetch thread is not running.\n");
}
}
void MyFrame::OnFetchComplete(wxThreadEvent& event)
{
void* buffer = event.GetPayload<void*>();
wxString label = event.GetString();
wxString s = wxString::Format("%s fetched.\n",label);
m_msgArea->AppendText(s);
m_gauge->SetValue(100);
free(buffer);
}
void MyFrame::OnFetchProgress(wxThreadEvent& event)
{
wxString perc = event.GetString();
wxString s = wxString::Format("FetchProgress %s .\n",perc);
m_msgArea->AppendText(s);
m_gauge->SetValue(event.GetExtraLong());
}
class MyApp : public wxApp
{
public:
virtual bool OnInit()
{
::wxInitAllImageHandlers();
MyFrame* frame = new MyFrame();
frame->Show();
return true;
}
};
wxIMPLEMENT_APP(MyApp);
Also, the options set in CurlCtrl::CurlCtrl are based on using the windows built in cert store for https. I think slightly different options would be needed for linux and mac, but I'm not sure what changes will be needed.