java C/S架构的客户端自动更新实现

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/sinat_34820292/article/details/81513611

前言:

用java开发的客户端,还在不断开发完善中,客户有几十个时,如果挨个通知下载更新,就太麻烦了,于是实现了一个更新功能,以节省更新的人力物力。

实现思路:

  1. 从服务器获取最新版本号,和当前版本判断,从而判断出是否需要更新
  2. 从服务器下载最新版本的jar文件到临时文件夹(所有java代码,包含更新的这一段,都导出为可执行的jar)
  3. 关闭当前程序,复制临时文件夹的新版本覆盖运行目录文件,启动程序(笔者最开始担心这个,敲黑板!,下面这句是重点)

第3点在实现时有疑虑,经过试验得知可行:因为Runtime.getRuntime().exec("windows运行命令");不阻塞,既执行到此句后可继续往后运行,以便关闭当前程序,完成覆盖重启

实现过程:

1. 在服务器端保存版本号:

在http服务器上用json文件存放最新版本信息: version.json

{
	"version":2.0,
	"desc":"1.引入自动跟新功能。\r\n2. 提高通讯效率。\r\n3. 修复若干bug。",
	"date":"2018-8-8 22:00:00"
}

2. 在服务器上存放最新版本程序:

因为程序包含 exe外壳,程序核心jar文件和jre运行环境,每次更新全部下载一遍很浪费服务器资源,我们仅仅在http服务器存放jar文件,并且打包exe时不把jar文件包含进去,仅仅用于调用 jre/bin/java.exe执行jar文件。
要实现上述运行方式,推荐一个软件,可以方便的把bat脚本打包成exe,来启动jar,可以实现不弹出cmd窗口,管理员权限运行,软件图标,运行目录等 https://download.csdn.net/download/sinat_34820292/10591678

3. 客户端实现下载和重启:

package com.your;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;

import com.alibaba.fastjson.JSONObject; // 版本用json存的
import com.your.server.Main; //你的主程序,下面要用来关闭程序实现重启,请读者
//自己实现关闭程序方法,使用思路为主程序开启时调用本类相关方法,完成更新,截图中的
//弹窗根据自己的业务去实现,如监测到新版本,则弹窗提示,点击确定后下载新版本,下载完了提示重启软件

public class Upgrader {
	public static float currentversion = 2.0f;//当前版本号
	public static float newversion = currentversion; //最新版本号
	public static boolean downloaded = false;//下载完成与否
	public static boolean errored = false;//下载出错与否
	public static String versinurl = "http://your.server/version/version.json"; //版本存放地址
	public static String jarurl = "http://your.server/download/celent/Main.jar"; // jar存放地址
	public static String string2dowload = "http://your.server/完整版本下载链接用于下载失败时调用浏览器完成下载.exe"; //备用更新方案
	public static String description = "";//新版本更新信息
	
	/**
	 * 静默下载最新版本
	 */
	public static void dowload() {
		try {
			downLoadFromUrl(jarurl, "dowloadtmp", "tmp");
			downloaded = true;
		} catch (Exception e) {
			downloaded = false;
			errored = true;
			e.printStackTrace();
		}
		 
	}
	
	/**
	 * 重启完成更新
	 */
	public static void restart() {
		try {
			Runtime.getRuntime().exec("cmd /k start .\\update.bat");
			Main.close(); //关闭程序以便重启
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 获取最新版本号
	 */
	public static void getnewversion() {
		String json = sendGetRequest(versinurl);
		JSONObject ob =  JSONObject.parseObject(json);
		newversion = ob.getFloat("version");
		description = ob.getString("desc");
	}
	
	/**
	 * 启动后自动更新
	 */
	public static void autoupgrade() {
		getnewversion();
		dowload();
		restart();
	}

	/**
	 * 发get请求,获取文本
	 * @param getUrl
	 * @return 网页context
	 */
	public static String sendGetRequest(String getUrl) {
		StringBuffer sb = new StringBuffer();
		InputStreamReader isr = null;
		BufferedReader br = null;
		try {
			URL url = new URL(getUrl);
			URLConnection urlConnection = url.openConnection();
			urlConnection.setAllowUserInteraction(false);
			isr = new InputStreamReader(url.openStream(),"UTF-8");
			br = new BufferedReader(isr);
			String line;
			while ((line = br.readLine()) != null) {
				sb.append(line);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		return sb.toString();
	}
	
	
	/**
	 * 从网络Url中下载文件
	 * 
	 * @param urlStr
	 * @param fileName
	 * @param savePath
	 * @throws IOException
	 */
	public static void downLoadFromUrl(String urlStr, String fileName, String savePath) throws IOException {
		URL url = new URL(urlStr);
		HttpURLConnection conn = (HttpURLConnection) url.openConnection();
		// 设置超时间为3秒
		conn.setConnectTimeout(3 * 1000);
		// 防止屏蔽程序抓取而返回403错误
		conn.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)");

		// 得到输入流
		InputStream inputStream = conn.getInputStream();
		// 获取自己数组
		byte[] getData = readInputStream(inputStream);

		// 文件保存位置
		File saveDir = new File(savePath);
		if (!saveDir.exists()) {
			saveDir.mkdir();
		}
		File file = new File(saveDir + File.separator + fileName);
		FileOutputStream fos = new FileOutputStream(file);
		fos.write(getData);
		if (fos != null) {
			fos.close();
		}
		if (inputStream != null) {
			inputStream.close();
		}

		System.out.println("info:" + url + " download success");

	}

	/**
	 * 从输入流中获取字节数组
	 * 
	 * @param inputStream
	 * @return
	 * @throws IOException
	 */
	public static byte[] readInputStream(InputStream inputStream) throws IOException {
		byte[] buffer = new byte[1024];
		int len = 0;
		ByteArrayOutputStream bos = new ByteArrayOutputStream();
		while ((len = inputStream.read(buffer)) != -1) {
			bos.write(buffer, 0, len);
		}
		bos.close();
		return bos.toByteArray();
	}
}

4. 更新脚本 update.bat

@echo off 
  if "%1" == "h" goto begin 
  mshta vbscript:createobject("wscript.shell").run("%~nx0 h",0)(window.close)&&exit 
  :begin 
  
@ping 127.0.0.1 -n 4 & mv .\tmp\dowloadtmp  .\source\Main.jar & .\启动软件.exe

前面那一段用于让控制台窗口在程序启动后消失
@ping 127.0.0.1 -n 4用于粗略延时4秒,避免开始复制时 Main.jar没有运行结束

------------ 分割线 -----------------
2018年8月21日更新:
上述的更新脚本: mv命令在win10上可以运行,但是到了win7/xp上就不管用了,所以换用xcopy /s/e/y,而且我的项目关闭不需要4s,所以重启延时改为1秒,即脚本改为:

@echo off 
  if "%1" == "h" goto begin 
  mshta vbscript:createobject("wscript.shell").run("%~nx0 h",0)(window.close)&&exit 
  :begin 
  
@ping 127.0.0.1 -n 1 & xcopy /s/e/y .\tmp\dowloadtmp  .\source\Main.jar & .\启动软件.exe

------------分割线结束-------------

5. 其他的补充说明:

1. 上文提到的,使用BatToExe把启动jar程序的bat转化为exe可执行程序,转化的bat代码如下
.\jre\bin\javaw.exe -jar .\source\Main.jar

这里写图片描述

2. 弹窗展示版本示例

这里写图片描述
一个简单的弹窗就不贴代码了
关于怎么做java程序的外壳,方便的开发java程序的界面,可以参考笔者的这篇博客
https://blog.csdn.net/sinat_34820292/article/details/80808126

展开阅读全文

没有更多推荐了,返回首页