electron-vue 开发(7) electron软件的升级更新

2,641 阅读3分钟

前言

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;
}

运行

image.png

image.png

image.png

image.png

demo

这个软件的升级逻辑可以适用于任何软件的升级功能,并可以自定义配置,demo放在了网盘里: 链接:pan.baidu.com/s/1d6-dG5m1… 提取码:9wt4