需求:实现远程接口的账户验证后,获得cookis的值,二次跳转实现数据的传输
单次数据发送:
public static void save() {
// 登陆 Url
String loginUrl = "https://ip:port/js/sso/ycsrlinterface/27f5a6d2dcca4741063f5a1e68e2d4a0?url=/a/sys/office/listData&relogin=true";
// 需登陆后访问的 Url
String dataUrl = "https://ip:port/js/a/sz/szUserTemperatr/save";
HttpClient httpClient = new HttpClient();
// 模拟登陆,按实际服务器端要求选用 Post 或 Get 请求方式
PostMethod postMethod = new PostMethod(loginUrl);
// 设置登陆时要求的信息,用户名和密码
postMethod.setParameter("username","xxxxx");
postMethod.setParameter("token","xxxxxx");
postMethod.setRequestHeader("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8");
try {
// 设置 HttpClient 接收 Cookie,用与浏览器一样的策略
httpClient.getParams().setCookiePolicy(CookiePolicy.BROWSER_COMPATIBILITY);
int statusCode = httpClient.executeMethod(postMethod);
// 获得登陆后的 Cookie
Cookie[] cookies = httpClient.getState().getCookies();
StringBuffer tmpcookies = new StringBuffer();
for (Cookie c : cookies) {
tmpcookies.append(c.toString());
// System.out.println("cookies = " + c.toString());
}
if (statusCode == 302) { //重定向到新的URL
System.out.println("模拟登录成功");
// 进行登陆后的操作
postMethod = new PostMethod(dataUrl);
// 每次访问需授权的网址时需带上前面的 cookie 作为通行证
postMethod.setRequestHeader("cookie", tmpcookies.toString());
// 你还可以通过 PostMethod/GetMethod 设置更多的请求后数据
// 例如,referer 从哪里来的,UA 像搜索引擎都会表名自己是谁,无良搜索引擎除外
postMethod.setRequestHeader("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8");
postMethod.setParameter("bname", "温度计名称");
postMethod.setParameter("temperatureId", "温度计 id");
postMethod.setParameter("collectTime", "2020-11-04");
postMethod.setParameter("data", "温度");
postMethod.setParameter("cyName", "供热公司名称");
postMethod.setParameter("phone", "联系电话");
postMethod.setParameter("userName", "住户姓名");
postMethod.setParameter("genTime", "2020-11-04");
postMethod.setParameter("regionName", "区域");
postMethod.setParameter("cname", "小区名称");
postMethod.setParameter("genTime", "2020-11-04");
postMethod.setParameter("genTime", "2020-11-04");
int statusCodes = httpClient.executeMethod(postMethod);
if(statusCodes == 200){
// 打印出返回数据,检验一下是否成功
String text = postMethod.getResponseBodyAsString();
System.out.println("单条数据传输成功"+text);
}
} else {
new RuntimeException("登录失败");
}
} catch (Exception e) {
e.printStackTrace();
}
}
批量数据传输:
public static void main(String[] args){
String username="xxxx";
String token="xxxxxxx";
// 登陆 Url
String loginUrl = "https://ip:port/js/sso/"+username+"/"+token+"?url=/a/sys/office/listData&relogin=true";
// 需登陆后访问的 Url
String dataUrl = "https://ip:port/js/a/sz/szUserTemperatr/saveAll";
HttpClient httpClient = new HttpClient();
// 模拟登陆,按实际服务器端要求选用 Post 或 Get 请求方式
PostMethod postMethod = new PostMethod(loginUrl);
postMethod.setRequestHeader("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8");
try {
// 设置 HttpClient 接收 Cookie,用与浏览器一样的策略
httpClient.getParams().setCookiePolicy(CookiePolicy.BROWSER_COMPATIBILITY);
int statusCode = httpClient.executeMethod(postMethod);
// 获得登陆后的 Cookie
Cookie[] cookies = httpClient.getState().getCookies();
StringBuffer tmpcookies = new StringBuffer();
for (Cookie c : cookies) {
tmpcookies.append(c.toString());
System.out.println("cookies = " + c.toString());
}
if (statusCode == 302) { //重定向到新的URL
// 进行登陆后的操作
postMethod = new PostMethod(dataUrl);
// 每次访问需授权的网址时需带上前面的 cookie 作为通行证
postMethod.setRequestHeader("cookie", tmpcookies.toString());
postMethod.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
List<Map<String, Object>> list = new ArrayList<>();
Map<String, Object> map = new LinkedHashMap<>();
map.put("temperatureId",123456789541258L);
map.put("collectTime", "2020-10-22 15:30:00");
map.put("data", "21.18");
map.put("cyName", "供热公司名称");
map.put("phone", "联系电话");
map.put("userName", "住户姓名");
map.put("genTime", "2020-10-22 15:30:00");
map.put("regionName", "区域");
map.put("cname", "小区名称");
map.put("houseNumber", "10-01");
map.put("pumpStationName", "站点名称");
map.put("recordId", "9daa76068e831bb0e21516ce84f519d111");
list.add(map);
String jsonStr = JSONObject.toJSONString(list);
RequestEntity se = new StringRequestEntity(jsonStr ,"application/json" ,"UTF-8");
postMethod.setRequestEntity(se);
postMethod.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, new DefaultHttpMethodRetryHandler());
postMethod.getParams().setParameter(HttpMethodParams.SO_TIMEOUT, 50000);//设置超时时间
int statusCodes = httpClient.executeMethod(postMethod);
String text=postMethod.getResponseBodyAsString();
if(statusCodes == 200){
System.out.println("批量数据传输成功"+text);
}
} else {
logger.info("远程校验失败,账户或密码错误,无法建立连接");
}
} catch (IOException e) {
e.printStackTrace();
}
}
业务开发过程中遇到的问题和解决方法:
因为是在已有的项目上开发新的需求,无法实现大规模的修改和增加新的插件,会影响已有功能的实现
所以只能使用基础功能实现项目中的要求,如果本身是单独一个模块,建议使用大佬的工具类比较方便
该项目基于HttpClient-4.4.1封装的一个工具类,支持插件式配置Header、插件式配置httpclient对象,这样就可以方便地自定义header信息、配置ssl、配置proxy等。
需求新增需求:
要求,实现配置网络链接,进行定时数据发送,未配置改数据链接,不执行数据发送
实现每次发送数据的结果响应打印输出文件记录数据
修改的代码:
@Component
public class TemperatureMeasurementDataTask {
protected static final Logger logger = Logger.getLogger(BuildingTerminalsHistoryTask.class);
//注入service实现类
@Resource(name = "buildingTerminalsHistoryService")
private BuildingTerminalsHistoryService buildingTerminalsHistoryService;
//修改发送数据的方法为静态工具类,只执行登录然后发送数据任务
public static String TemperatureMeasurementDataTask(String jsonStr, Map<String, String> map) {
long startTime = System.currentTimeMillis();
logger.info("TemperatureMeasurementDataTask");
String loginUrl = map.get("loginUrl");
String dataUrl = map.get("dataUrl");
if (!StringUtil.isEmptyString(loginUrl) && !StringUtil.isEmptyString(dataUrl)) {
Map<String, String> maps = new HashMap<>();
HttpClient httpClient = new HttpClient();
// 模拟登陆,按实际服务器端要求选用 Post 或 Get 请求方式
PostMethod postMethod = new PostMethod(loginUrl);
postMethod.setRequestHeader("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8");
try {
// 设置 HttpClient 接收 Cookie,用与浏览器一样的策略
httpClient.getParams().setCookiePolicy(CookiePolicy.BROWSER_COMPATIBILITY);
int statusCode = httpClient.executeMethod(postMethod);
// 获得登陆后的 Cookie
Cookie[] cookies = httpClient.getState().getCookies();
StringBuffer tmpcookies = new StringBuffer();
for (Cookie c : cookies) {
tmpcookies.append(c.toString());
// System.out.println("cookies = " + c.toString());
}
if (statusCode == 302) { //重定向到新的URL
// 进行登陆后的操作
postMethod = new PostMethod(dataUrl);
// 每次访问需授权的网址时需带上前面的 cookie 作为通行证
postMethod.setRequestHeader("cookie", tmpcookies.toString());
postMethod.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
RequestEntity se = new StringRequestEntity(jsonStr, "application/json", "UTF-8");
postMethod.setRequestEntity(se);
postMethod.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, new DefaultHttpMethodRetryHandler());
postMethod.getParams().setParameter(HttpMethodParams.SO_TIMEOUT, 500000);//设置超时时间
int statusCodes = httpClient.executeMethod(postMethod);
String text = postMethod.getResponseBodyAsString();
// System.out.println(text);
if (statusCodes == 200) {
return "批量数据传输结果:" + text;
}
} else {
return "远程校验失败,账户或密码错误,无法建立连接";
}
} catch (IOException e) {
e.printStackTrace();
}
return "发送数据异常";
}
return "暂无登录链接和数据发送链接";
}
//执行定时任务,运行数据发送的业务逻辑同时将结果进行发送
@SuppressWarnings({"unchecked", "rawtypes"})
@Scheduled(cron = "0 0 * * * ?")//默认使用一小时发送一次数据参数
public void automaticSending() {
// 调用最近一个小时的最新数据
List<Map<String, Object>> list = buildingTerminalsHistoryService.getTemperatureMeasurementDataList();
Map<String, String> stringStringMap = readProp();
// 定义发送的大小限制
int flag = 499;//每次取的数据
int size = list.size();
int temp = size / flag + 1;
boolean special = size % flag == 0;
List<Map<String, Object>> cutList = null;
// 分割查询到的list集合
for (int i = 0; i < temp; i++) {
if (i == temp - 1) {
if (special) {
break;
}
cutList = list.subList(flag * i, size);
} else {
cutList = list.subList(flag * i, flag * (i + 1));
}
// 每次发送不超过499条数据
String jsonStr = JSONObject.toJSONString(cutList);
printOut(jsonStr);
// 处理响应结果
String result = TemperatureMeasurementDataTask(jsonStr, stringStringMap);
printOut(result);
}
}
// 读取配置文件中的登录地址和项目批量发送数据接口
public Map<String, String> readProp() {
// 创建输入流
FileInputStream fis = null;
Map<String, String> map = null;
try {
// 创建Properties文件对象
Properties pros = new Properties();
// 使用输入流加载配置文件信息
String url = Thread.currentThread().getContextClassLoader().getResource("").toURI().getPath();
fis = new FileInputStream(url + "heating.properties");
// 使用流信息加载配置文件
pros.load(fis);
String loginUrl = pros.getProperty("loginUrl");
String dataUrl = pros.getProperty("dataUrl");
map = new HashMap<>();
map.put("loginUrl", loginUrl);
map.put("dataUrl", dataUrl);
} catch (IOException e) {
e.printStackTrace();
} catch (URISyntaxException e) {
e.printStackTrace();
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return map;
}
public static void printOut(String str) {
RandomAccessFile raf = null;
try {
String url = Thread.currentThread().getContextClassLoader().getResource("").toURI().getPath();
//创建目录
File dir = new File(url + "record");
if (!dir.exists()) {
dir.mkdir();
}
Date date = new Date();
String strs = DateUtil.fromDateToString(date).substring(0, 10) + ".logger";
//创建文件
File file = new File(dir, strs);
if (!file.exists()) {
file.createNewFile();
}
//实例化RandomAccessFile对象
raf = new RandomAccessFile(file, "rw");
//打开文件时指针位置在最前,即
raf.seek(file.length());
SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
//格式化
String format = sdf1.format(date);
String msg = System.getProperty("line.separator") + format + str;
byte[] bytes = msg.getBytes();
raf.write(bytes);
// System.out.println(raf.getFilePointer());
} catch (URISyntaxException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (raf != null) {
try {
//操作结束后一定要关闭文件
raf.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
出现的问题:
①:无法获取配置文件的信息
采用获取当前项目的路径的方法,拼接文件的名称,无论部署环境如何迁移,都可以定位当前配置文件位置
②:RandomAccessFile打印输出的时候,开始使用writeUTF的方法直接打印String字符串,然后结尾处一直存在乱码。使用切割方法对最后的乱码进行裁剪,都不成功,反倒截到返回的字符串
修改方法,使用字节流的方法进行打印输出 raf.write(bytes)
新增需求:项目迭代中,需求要求实现动态的定时任务的功能配置,
目前思路有两个,一个是使用反射的方法,对这个类的配置的注解的参数和这个定时任务的方法进行动态获取,然后重新生成对象
另一个思路是使用定时任务的配置类的方法实现定时任务的功能完善。String框架在启动的时候就对所有的注解进行扫描注册,所以要修改定时任务的执行频率,要么使用反射,去修改内存中的执行定时的频率的对象,要不然使用配置类去实现,每次的执行频率可以使用去数据库后期配置信息