前言
electron软件的更新,在网上查找过很多版本,我都不是很太理解。而本身我自己也是搞c++的,所以搞个原生的升级软件不是小意思吗。索性用vs2017+duilib写了一个升级软件。
思路
其实每次electron软件我们的业务逻辑都是在 {App}/resources/app 目录下的,因此只要把打包出来的这个目录替换我们最新的文件就可以了
逻辑
这个升级软件的逻辑很简单,用户打开软件其实先打开的是升级软件,升级软件会配置升级软件的url地址以及本地的软件版本,同时在服务器端,也会放一个zip格式的升级包以及一个名为version.json的文件,里面记录了压缩包中软件的版本号。
升级软件如果检测本地的版本号小于服务器端的版本号就会下载zip压缩包到本地,并根据setting.ini中的配置,解压到指定目录,完成之后打开指定目录的应用程序。
代码
int CFirstpage::StartMain()
{
GetSettingIni();
::DeleteFile(string(GetAppPath(NULL) + "/unzip/version.json").c_str());
::DeleteFile(string(GetAppPath(NULL) + "/unzip/zip.zip").c_str());
label_show->SetText(_T("正在检查文件目录..."));
// 0 检查 目录
CUnzipper unzipper;
unzipper.CreateFolder(string(GetAppPath(NULL) + "/unzip/").c_str());
// 0-1 清空目录
label_show->SetText(_T("正在检查本地版本..."));
//1. 获取本地信息 服务器地址 本地版本号
GetLocalInfo(m_strVersionLocal, m_strCity, m_strPlatformUrl);
sVersionFileName = string(DST_VERSION_JSON) + "version_" + m_strCity + ".json";
m_strVersionDown = GetAppPath(NULL) + "\\unzip\\version.json";
label_show->SetText(_T("正在下载文件更新列表..."));
//2. 下载远端版本文件
HANDLE hThread = CreateThread(NULL, 0, DownloadThread, this, 0, NULL);
CloseHandle(hThread);
return 0;
}
使用libcurl库下载服务端的压缩包,并显示进度
static size_t DownloadCallback(void* pBuffer, size_t nSize, size_t nMemByte, void* pParam)
{
//把下载到的数据以追加的方式写入文件(一定要有a,否则前面写入的内容就会被覆盖了)
FILE* fp = NULL;
fopen_s(&fp, g_pFirstpage->m_strVersionDown.c_str(), "ab+");
size_t nWrite = fwrite(pBuffer, nSize, nMemByte, fp);
fclose(fp);
return nWrite;
}
bool gDownload_json = false;
bool gDownload_zip = false;
static int ProgressCallback(void *clientp, double dltotal, double dlnow, double ultotal, double ulnow)
{
if (dltotal > -0.1 && dltotal < 0.1)
return 0;
int nPos = (int)((dlnow / dltotal) * 100);
//通知进度条更新下载进度
//::PostMessage(g_pFirstpage->GetHWND(), WM_PROGRESS_UPDATE, nPos, 0);
//::Sleep(10);
if (nPos >= 100)
{
if (!gDownload_json)
{
gDownload_json = true;
::Sleep(500);
::PostMessage(g_pFirstpage->GetHWND(), WM_DOWNLOAD_ZIP, nPos, 0);
}
}
return 0;
}
static size_t DownloadCallbackZip(void* pBuffer, size_t nSize, size_t nMemByte, void* pParam)
{
//把下载到的数据以追加的方式写入文件(一定要有a,否则前面写入的内容就会被覆盖了)
FILE* fp = NULL;
fopen_s(&fp, g_pFirstpage->m_strZipDown.c_str(), "ab+");
size_t nWrite = fwrite(pBuffer, nSize, nMemByte, fp);
fclose(fp);
return nWrite;
}
static int ProgressCallbackZip(void *clientp, double dltotal, double dlnow, double ultotal, double ulnow)
{
if (dltotal > -0.1 && dltotal < 0.1)
return 0;
int nPos = (int)((dlnow / dltotal) * 100);
//通知进度条更新下载进度
::SendMessage(g_pFirstpage->GetHWND(), WM_PROGRESS_UPDATE, nPos, 0);
::Sleep(10);
if (nPos >= 100)
{
if (!gDownload_zip)
{
gDownload_zip = true;
::Sleep(500);
::SendMessage(g_pFirstpage->GetHWND(), WM_UNZIP, nPos, 0);
}
}
return 0;
}
DWORD WINAPI DownloadThread(LPVOID lpParam)
{
CFirstpage* pFirstpage = (CFirstpage*)lpParam;
//Sleep(10000);
//初始化curl,这个是必须的
CURL* curl = curl_easy_init();
curl_easy_setopt(curl, CURLOPT_URL, string(pFirstpage->m_strPlatformUrl+ sVersionFileName).c_str());
//设置接收数据的回调
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, DownloadCallback);
// 设置重定向的最大次数
curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 5);
// 设置301、302跳转跟随location
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, 7777);
//设置进度回调函数
curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, ProgressCallback);
if (pFirstpage->m_bIsProxy)
{
curl_easy_setopt(curl, CURLOPT_PROXYAUTH, CURLAUTH_BASIC); //代理认证模式
curl_easy_setopt(curl, CURLOPT_PROXY, pFirstpage->m_strProxyIp.c_str()); //代理服务器地址
curl_easy_setopt(curl, CURLOPT_PROXYPORT, pFirstpage->m_nProxyPort); //代理服务器端口
curl_easy_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_HTTP); //使用http代理模式
}
//开始执行请求
CURLcode retcCode = curl_easy_perform(curl);
//查看是否有出错信息
const char* pError = curl_easy_strerror(retcCode);
//清理curl,和前面的初始化匹配
curl_easy_cleanup(curl);
if (retcCode != CURLE_OK)
{
pFirstpage->label_show->SetText(pError);
Sleep(1000);
pFirstpage->deq_Msg.push_back(_T("StartExe"));
}
return 0;
}
DWORD WINAPI DownloadThreadZip(LPVOID lpParam)
{
CFirstpage* pFirstpage = (CFirstpage*)lpParam;
//Sleep(10000);
//初始化curl,这个是必须的
CURL* curl = curl_easy_init();
curl_easy_setopt(curl, CURLOPT_URL, string(pFirstpage->m_strPlatformUrl + DST_VERSION_PATH + sZipFileName).c_str());
//设置接收数据的回调
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, DownloadCallbackZip);
// 设置重定向的最大次数
curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 5);
// 设置301、302跳转跟随location
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
//设置进度回调函数
curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, ProgressCallbackZip);
if (pFirstpage->m_bIsProxy)
{
curl_easy_setopt(curl, CURLOPT_PROXYAUTH, CURLAUTH_BASIC); //代理认证模式
curl_easy_setopt(curl, CURLOPT_PROXY, pFirstpage->m_strProxyIp.c_str()); //代理服务器地址
curl_easy_setopt(curl, CURLOPT_PROXYPORT, pFirstpage->m_nProxyPort); //代理服务器端口
curl_easy_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_HTTP); //使用http代理模式
}
//开始执行请求
CURLcode retcCode = curl_easy_perform(curl);
//查看是否有出错信息
const char* pError = curl_easy_strerror(retcCode);
//清理curl,和前面的初始化匹配
curl_easy_cleanup(curl);
if (retcCode != CURLE_OK)
{
pFirstpage->label_show->SetText(pError);
Sleep(1000);
pFirstpage->deq_Msg.push_back(_T("StartExe"));
}
//p_mainWnd->Close();
return 0;
}
运行
demo
这个软件的升级逻辑可以适用于任何软件的升级功能,并可以自定义配置,demo放在了网盘里: 链接:pan.baidu.com/s/1d6-dG5m1… 提取码:9wt4