【huTool】真的好用,面对接口开发之再来一弹

    技术2023-07-03  119

    小公司的悲哀,维护项目和小需求不断。。

    整理一下,算作纪念

    需求:第三方需要接口传输图片流到特定的摄像头下 分析:

    /** * 开发接口: * 1.接收主要:图片(小图,大图),时间,设备id -> Base64(小图,大图),时间戳,设备id * 2.调用平台登录,全部相机接口 -> 相机集合[相机id,相机唯一标识] * 3.遍历相机集合根据【设备id = 唯一标识】锁定入参:[相机id,Base64(小图,大图),时间戳] * 4.调用入库接口,传入入参 * 5.异常处理,数据包装返回 */

    重点:

    数据格式:multipart/form-data,区别以往的application/json,text/plain请求参数格式,需要传文件啦涉及到大数据,缓存起来,缩减以后的响应时间日志也要好好写啦,区别以往,增加logback.xml配置文件全局异常要继续hold住啊

    好了,咱们代码里详聊~ ``首先,实体类

    /** * Created by DQ on 2020/6/23/023. * 请求类 */ @Data public class RequestDTO { private String faceid; private String deviceid; private String devicename; private String locationid; private String locationname; private String lng; private String lat; private String identification; private String passtime; private MultipartFile facepic; private MultipartFile backgroudpic; private String rect; } /** * 响应类 */ @Data public class BaseResponse { // Y 错误码 0:成功 其它:失败 protected Integer errcode; // Y 错误描述 protected String errmsg; public BaseResponse() { this(ResponseEnum.SUCCESS); } public BaseResponse(ResponseEnum errorCode) { this.errcode = errorCode.getRtn(); this.errmsg = errorCode.getMessage(); } public BaseResponse(ResponseEnum errorCode, String message) { this.errcode = errorCode.getRtn(); this.errmsg = message; } public boolean valid() { return 0 == this.errcode; } } /** * 响应状态码枚举类 */ public enum ResponseEnum { SUCCESS(0, "OK"), SERVER_ERROR(1, "服务器异常,请稍后重试"), BAD_REQUEST_PARAMETER(-1, "图片参数错误"), NO_MATCH_DEVICEID(-1000, "deviceid与平台无匹配"), BAD_REQUEST(-2, "请求错误"), MEDIA_TYPE_NOT_SUPPORTED(-3, "请求的Content-Type错误"), METHOD_NOT_SUPPORTED(-4, "请求方法不支持"), ; private Integer rtn; private String message; ResponseEnum(Integer rtn, String message) { this.rtn = rtn; this.message = message; } public Integer getRtn() { return rtn; } public String getMessage() { return message; } } /** * Created by DQ on 2020/6/24/024. * 要被缓存的类 */ @Data public class Camera { private Integer id; //对应摄像头的唯一标识 外部deviceid 内部police_unit private String police_unit; public Camera(Integer id, String police_unit) { this.id = id; this.police_unit = police_unit; } public Camera() { } }

    重头戏来咯,controller和service

    /** * Created by DQ on 2020/6/24/024. */ @RestController @RequestMapping("/img") @Api(value = "ImgFlow", tags = {"图片流入库Controller"}) public class ImgFlow { private static Logger logger = LoggerFactory.getLogger(ImgFlow.class); @Autowired FlowService flowService; @RequestMapping(value = "/flow", method = RequestMethod.POST) @ApiOperation(value = "图片流入库接口") public BaseResponse handleFileUpload(@RequestParam(value = "deviceid") String deviceid, @RequestParam(value = "passtime") String passtime, @RequestParam(value = "facepic") MultipartFile facepic, @RequestParam(value = "backgroudpic") MultipartFile backgroudpic, @RequestParam(value = "faceid", required = false) String faceid, @RequestParam(value = "devicename", required = false) String devicename, @RequestParam(value = "locationid", required = false) String locationid, @RequestParam(value = "locationname", required = false) String locationname, @RequestParam(value = "lng", required = false) Long lng, @RequestParam(value = "lat", required = false) Long lat, @RequestParam(value = "identification", required = false) String identification, @RequestParam(value = "rect", required = false) String rect ) throws IOException, InterruptedException { logger.info("deviceid:" + deviceid); logger.info("passtime:" + passtime); String faceFilename = facepic.getOriginalFilename(); String backFilename = backgroudpic.getOriginalFilename(); logger.info("facepic:" + faceFilename); logger.info("backgroudpic:" + backFilename); if(StrUtil.isBlank(faceFilename) || StrUtil.isBlank(backFilename)){ return new BaseResponse(ResponseEnum.BAD_REQUEST_PARAMETER); }else { if(faceFilename.contains(".jpg") || faceFilename.contains(".png") || faceFilename.contains(".bmp")){ if(backFilename.contains(".jpg") || backFilename.contains(".png") || backFilename.contains(".bmp")){ return flowService.FlowIt(deviceid, passtime, facepic, backgroudpic); } return new BaseResponse(ResponseEnum.BAD_REQUEST_PARAMETER); } return new BaseResponse(ResponseEnum.BAD_REQUEST_PARAMETER); } } } /** * Created by DQ on 2020/6/23/023. */ @Service public class FlowService { private static Logger logger = LoggerFactory.getLogger(FlowService.class); @Autowired HttpUtils httpUtils; public BaseResponse FlowIt(String deviceid, String passtime, MultipartFile facepic, MultipartFile backgroudpic) throws InterruptedException { BaseResponse baseResponse = new BaseResponse(); httpUtils.loginFp(); ArrayList<Camera> cameras = httpUtils.getCameras(); int j = 0; for (Camera camera : cameras) { if (deviceid.equals(camera.getPolice_unit())) { String body = JSONUtil.createObj() .set("camera_id", camera.getId()) .set("face_image_content_base64", toBase64(facepic)) .set("picture_image_content_base64", toBase64(backgroudpic)) .set("timestamp", toTimeStamp(passtime)).toString(); logger.info(body); baseResponse = httpUtils.flowImg(body); } else { j++; } } if (j == cameras.size()) { baseResponse = new BaseResponse(ResponseEnum.NO_MATCH_DEVICEID); } return baseResponse; } private String toBase64(MultipartFile someone) { return Base64.encode(tarnsToFile(someone)); } private long toTimeStamp(String time) { DateTime parse = DateUtil.parse(time); return parse.getTime()/1000; } /* * MultipartFile -> File * */ private File tarnsToFile(MultipartFile multipartFile){ File file = null; try { file=File.createTempFile("tmp", null); multipartFile.transferTo(file); file.deleteOnExit(); } catch (HttpException | IOException e) { e.printStackTrace(); } return file; } }

    插个话,最近在看本书,《会说话的代码》,讲的是代码的规范方面,讲到注释只是辅助工具,好的代码不需要注释。正在往这方面努力

    下面是util服务

    /** * Created by DQ on 2020/6/23/023. */ @Service public class HttpUtils { private static Logger logger = LoggerFactory.getLogger(HttpUtils.class); private static String FP_IP = getProps("fp.ip"); private static String FP_PORT = getProps("fp.port"); private static String loginName = getProps("fp.loginName"); private static String loginPwd = getProps("fp.loginPwd"); private static String loginUrl = getProps("fp.loginUrl"); private static String flowUrl = getProps("mts.flowUrl"); private static String asyncUrl = getProps("fp.asyncUrl"); private static String cameraUrl = getProps("fp.cameraUrl"); private static String cameraCache = getProps("cameraList.cache"); private static String name = getProps("camera-device.name"); public static Cache<String, String> lruCache = CacheUtil.newLRUCache(3); public static Cache<String, ArrayList<Camera>> listCache = CacheUtil.newLRUCache(2); /* * 入库接口 * POST * */ public BaseResponse flowImg(String body) { BaseResponse baseResponse = new BaseResponse(); String fpUrl = formatFpUrl(flowUrl, FP_IP, "21100"); HttpRequest request = HttpRequest.post(fpUrl) .body(body) .timeout(10000); HttpResponse execute = request.execute(); JSONObject obj = JSONUtil.parseObj(execute.body()); Integer rtn = obj.getInt("rtn"); if (rtn != 0) { baseResponse.setErrcode(rtn); baseResponse.setErrmsg("入库失败"); } return baseResponse; } /* * 登录接口 * POST * */ public void loginFp() { if (StrUtil.isBlank(FP_IP) || StrUtil.isBlank(FP_PORT) || StrUtil.isBlank(loginName) || StrUtil.isBlank(loginPwd) || StrUtil.isBlank(loginUrl) || StrUtil.isBlank(flowUrl)) { logger.error("有配置为空!"); } if(StrUtil.isBlank(lruCache.get("cookie")) || StrUtil.isBlank(lruCache.get("cluster_id"))){ String fpUrl = formatFpUrl(loginUrl, FP_IP, FP_PORT); String password = DigestUtil.md5Hex(loginPwd); HttpRequest req = HttpRequest.post(fpUrl) .header("Content-Type", "application/json;charset=UTF-8") .body(JSONUtil.createObj() .set("name", loginName) .set("password", password) .toString()) .timeout(5000); HttpResponse execute = req.execute(); JSONObject resp = JSONUtil.parseObj(execute.body()); if (resp.getInt("rtn") == 0) { String session_id = resp.getStr("session_id"); String cluster_id = session_id.substring(session_id.indexOf("@") + 1); String cookie = "session_id=" + session_id; //默认保存3分钟 lruCache.put("cookie", cookie, 60 * 3 * 1000L); lruCache.put("cluster_id", cluster_id, 60 * 3 * 1000L); } } } /* * post获取result_id,并get获取到cameras * */ public ArrayList<Camera> getCameras() throws InterruptedException { if(!listCache.containsKey("camera") || listCache.get("camera").isEmpty()){ String fpUrl = formatFpUrl(asyncUrl, FP_IP, FP_PORT); ArrayList<Camera> list = new ArrayList<>(); String cookie = lruCache.get("cookie"); String cluster_id = lruCache.get("cluster_id"); String resultId = postResultId(cookie, cluster_id, fpUrl); String url = fpUrl + "?result_id=" + resultId; String camerabody = getCamera(url, cookie); JSONObject parseObj = JSONUtil.parseObj(camerabody); JSONArray cluster_results = parseObj.getJSONArray("cluster_results"); if (!cluster_results.isEmpty()) { JSONObject results = cluster_results.getJSONObject(0).getJSONObject("results"); JSONArray cameras = results.getJSONArray("cameras"); for (int i = 0; i < cameras.size(); i++) { Camera camera = new Camera(); String POLICY_CODE; JSONObject camerai = cameras.getJSONObject(i); Integer id = camerai.getInt("id"); if(camerai.containsKey(name)){ POLICY_CODE= camerai.getStr(name); }else { POLICY_CODE = camerai.getJSONObject("meta").getStr(name); } camera.setId(id); camera.setPolice_unit(POLICY_CODE); list.add(camera); } } //默认缓存12小时 listCache.put("camera", list, 60 * 60 * 12 * 1000L); return list; } logger.info(listCache.get("camera").toString()); return listCache.get("camera"); } private String postResultId(String cookie, String cluster_id, String fpUrl) { JSONObject request = JSONUtil.createObj() .set("cluster_id", cluster_id) .set("method", "GET") .set("payload", JSONUtil.createObj() .set("clusterId", cluster_id)) .set("url", "/website/face/camera-device"); JSONArray requests = JSONUtil.createArray().put(request); String body = JSONUtil.createObj() .set("requests", requests) .toString(); System.out.println(body); HttpRequest req = HttpRequest.post(fpUrl) .header("Content-Type", "application/json;charset=UTF-8") .header("Cookie", cookie) .body(body) .timeout(10000); HttpResponse execute = req.execute(); JSONObject parseObj = JSONUtil.parseObj(execute.body()); return parseObj.getStr("result_id"); } /* * get平台获取Cameras * */ private String getCamera(String url, String cookie) throws InterruptedException { HttpRequest request = HttpRequest.get(url) .header("Content-Type", "application/json;charset=UTF-8") .header("Cookie", cookie) .timeout(15000); Thread.sleep(3000L); return request.execute().body(); } /* * url处理类 * */ private static String formatFpUrl(String url, String ip, String port) { if (StrUtil.isNotBlank(url)) { url = url.replace("[IP]", ip); url = url.replace("[PORT]", port); } return url; } /* * 读取配置类 * */ private static String getProps(String key){ String path = System.getProperty("user.dir") + System.getProperty("file.separator") + "config.properties"; if(!new File(path).exists()){ logger.error("文件config.properties未找到!"); return null; } return new Props(path).getProperty(key); } }

    在utils里有很多需要注意的地方,很多可能存在的异常,我并没有管,也没有trycatch,我觉得开发环境就是要大胆暴漏这些exceptions,好好测试,大不了我们下面全局处理一下嘛。。

    /** * Created by DQ on 2020/6/23/023. */ @RestControllerAdvice public class GlobalExceptionHandler { private static Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class); /** * 系统异常捕获处理 */ @ExceptionHandler(Exception.class) // @ResponseBody public BaseResponse exception(Exception e) { logger.error(e.getMessage(), e.getCause()); //请求参数问题 if (e instanceof HttpMessageNotReadableException) { return new BaseResponse(ResponseEnum.BAD_REQUEST); } //请求Content type不支持 if (e instanceof HttpMediaTypeNotSupportedException) { return new BaseResponse(ResponseEnum.MEDIA_TYPE_NOT_SUPPORTED); } //请求方法不支持 if (e instanceof HttpRequestMethodNotSupportedException) { return new BaseResponse(ResponseEnum.METHOD_NOT_SUPPORTED); } return new BaseResponse(ResponseEnum.SERVER_ERROR); } }

    好了,非常简单处理掉MultipartFile与File的转换,调用接口还是需要多练习,最近准备学习一下spring自带的restTemplent,听说这货集成了JDK 自带的 HttpURLConnection,Apache 的 HttpClient和OKHttp3,还是蛮厉害的~~ 不是hutool的http请求库不好用,就是害怕高并发和大数据情况下不保险啊。。 我看公司项目里也有人用hutool,但是httpclient还是稳啊

    Processed: 0.011, SQL: 9