├── .gitignore ├── README.md ├── pom.xml └── src ├── main └── java │ └── com │ └── xcbeyond │ └── common │ ├── DateUtils.java │ ├── StringUtils.java │ ├── communication │ ├── http │ │ └── HttpClient.java │ └── socket │ │ ├── SocketClient.java │ │ └── SocketServer.java │ ├── data │ └── redis │ │ └── RedisUtils.java │ ├── file │ └── chunk │ │ ├── controller │ │ └── FileController.java │ │ └── service │ │ ├── FileService.java │ │ └── impl │ │ └── FileServiceImpl.java │ └── security │ └── JwtToken.java └── test └── java ├── com └── xcbeyond │ └── common │ ├── DateUtilsTest.java │ ├── StartApplication.java │ ├── communication │ └── socket │ │ ├── SocketClientTest.java │ │ └── SocketServerTest.java │ └── security │ └── JwtTokenTest.java └── resources └── application.properties /.gitignore: -------------------------------------------------------------------------------- 1 | .project 2 | .classpath 3 | /target/ 4 | .idea -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # common-utils 2 | 通用Java基础库,汇聚常用工具类 3 | 4 | # 手册 5 | 详细手册文档同步在[CSDN博客](https://blog.csdn.net/xcbeyond)、微信公众号("程序猿技术大咖(cxyjsdk)")更新。 6 | * [HTTP方式文件分片断点下载](https://mp.weixin.qq.com/s?__biz=MzA5MzUwOTY4NQ==&mid=2247484537&idx=1&sn=557a97d9abddc30164a416c7af72fd56&chksm=905d8523a72a0c357bcb453d256fcd5ba14f842d4ac472ec35ebe1268bf778301b05fbbca284&token=1646810344&lang=zh_CN#rd) 7 | 8 | # 微信公众号: 9 | 欢迎微信扫码下面二维码,关注微信公众号【程序猿技术大咖】,进行更多交流学习! 10 | 11 | ![cxyjsdk](https://img-blog.csdn.net/20180608091156204 "程序猿技术大咖") -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.xcbeyond.common 8 | common-utils 9 | 1.0 10 | 11 | 通用java基础库 12 | 13 | 14 | UTF-8 15 | UTF-8 16 | 1.8 17 | 2.0.7.RELEASE 18 | 1.2.47 19 | 20 | 21 | 22 | 23 | 24 | junit 25 | junit 26 | RELEASE 27 | test 28 | 29 | 30 | 31 | 32 | org.jsoup 33 | jsoup 34 | 1.11.3 35 | 36 | 37 | 38 | 39 | org.springframework.boot 40 | spring-boot-starter-web 41 | 42 | 43 | org.springframework.boot 44 | spring-boot-starter-data-redis 45 | 46 | 47 | 48 | com.alibaba 49 | fastjson 50 | ${fastjson.version} 51 | 52 | 53 | 54 | 55 | io.jsonwebtoken 56 | jjwt 57 | 0.9.0 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | org.springframework.boot 66 | spring-boot-dependencies 67 | ${spring-boot.version} 68 | pom 69 | import 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | org.apache.maven.plugins 78 | maven-compiler-plugin 79 | 3.6.0 80 | 81 | 1.8 82 | 1.8 83 | 84 | 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /src/main/java/com/xcbeyond/common/DateUtils.java: -------------------------------------------------------------------------------- 1 | package com.xcbeyond.common; 2 | 3 | import java.text.ParseException; 4 | import java.text.SimpleDateFormat; 5 | import java.util.Calendar; 6 | import java.util.Date; 7 | 8 | /** 9 | * 日期工具类 10 | * @Auther: xcbeyond 11 | * @Date: 2019/4/30 15:17 12 | */ 13 | public class DateUtils { 14 | 15 | /** 16 | * 获取当前时间,yyyy-MM-dd HH:mm:ss 17 | * @return 18 | */ 19 | public static String getCurrentDate() { 20 | //设置日期格式 21 | SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 22 | // new Date()为获取当前系统时间 23 | return df.format(new Date()); 24 | } 25 | 26 | /** 27 | * 根据输入的日期格式获得系统当前日期 28 | * @param format 29 | * 时间格式化,如:"yyyy-MM-dd","yyyy/MM/dd","yyyyMMdd" 30 | * @return 31 | */ 32 | public static String getCurrentDate(String format) { 33 | String date; 34 | try { 35 | SimpleDateFormat sdf = new SimpleDateFormat(format); 36 | date = sdf.format(new Date()); 37 | } catch (Exception e) { 38 | // 默认格式 39 | SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd"); 40 | date = sdf.format(new Date()); 41 | } 42 | return date; 43 | } 44 | 45 | /** 46 | * 获取当前时间(yyyy年MM月dd日) 47 | * @return 48 | */ 49 | public static String getYYYYMMDDCHN() { 50 | SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日"); 51 | return sdf.format(new Date()); 52 | } 53 | 54 | /** 55 | * 获取当前时间(yyyyMMdd) 56 | * @return 57 | */ 58 | public static String getYYYYMMDD() { 59 | SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); 60 | return sdf.format(new Date()); 61 | } 62 | 63 | /** 64 | * 获取当前时间(格式自定义) 65 | * @param type 时间格式化字符串,如:"yyyy-MM-dd" 66 | * @return 67 | */ 68 | public static String getYYYYMMDD(String type) { 69 | SimpleDateFormat sdf = new SimpleDateFormat(type); 70 | return sdf.format(new Date()); 71 | } 72 | 73 | /** 74 | * 获取当前时间(yyyyMMddHHmmssSSS) 75 | * @return 76 | */ 77 | public static String getYYYYMMDDHHMMssSSS() { 78 | SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS"); 79 | return sdf.format(new Date()); 80 | } 81 | 82 | /** 83 | * 获取当前时间 hh24mmss 84 | * @return 85 | */ 86 | public static String getHMS() { 87 | SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss"); 88 | return sdf.format(new Date()); 89 | } 90 | 91 | 92 | /** 93 | * 取得当天之后几个月的天数 94 | * @param yyyyMM 95 | * @return 96 | */ 97 | public static int getDaysOfMonthNext(String yyyyMM, int months) { 98 | Calendar rightNow = Calendar.getInstance(); 99 | SimpleDateFormat simpleDate = new SimpleDateFormat("yyyyMM"); 100 | int retValue = 0; 101 | for (int i = 0; i <= months; i++) { 102 | rightNow.add(rightNow.MONTH, i); 103 | yyyyMM = simpleDate.format(rightNow.getTime()); 104 | String now = getYYYYMMDD("yyyyMMdd"); 105 | if (i == 0) { 106 | int dd = Integer.parseInt(now.substring(6)); 107 | retValue += (getDaysOfMonth(yyyyMM) - dd + 1); 108 | } else if (i == months) { 109 | int dd = Integer.parseInt(now.substring(6)); 110 | retValue += dd - 1; 111 | } else { 112 | retValue += getDaysOfMonth(yyyyMM); 113 | } 114 | } 115 | return retValue; 116 | } 117 | 118 | /** 119 | * 取得某年某月有多少天 120 | * @param yyyyMM 121 | * 年月,注意时间格式 yyyyMM 122 | * @return 123 | */ 124 | public static int getDaysOfMonth(String yyyyMM) { 125 | Calendar rightNow = Calendar.getInstance(); 126 | SimpleDateFormat simpleDate = new SimpleDateFormat("yyyyMM"); // 如果写成年月日的形式的话,要写小d,如:"yyyy/MM/dd" 127 | try { 128 | rightNow.setTime(simpleDate.parse(yyyyMM)); // 要计算你想要的月份,改变这里即可 129 | } catch (ParseException e) { 130 | e.printStackTrace(); 131 | } 132 | int days = rightNow.getActualMaximum(Calendar.DAY_OF_MONTH); 133 | return days; 134 | } 135 | 136 | /** 137 | * 两时间相差的天数 注:日期格式yyyyMMdd 138 | * @param startDay 139 | * @param endDay 140 | * @return 141 | */ 142 | public static long getDiffDays(String startDay, String endDay) { 143 | SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd"); 144 | long days = 0; 145 | try { 146 | Date s = sdf.parse(startDay); 147 | Date e = sdf.parse(endDay); 148 | long sd = s.getTime(); 149 | long ed = e.getTime(); 150 | long diff; 151 | if (ed > sd) { 152 | diff = ed - sd; 153 | } else { 154 | diff = sd - ed; 155 | } 156 | days = diff / (1000 * 60 * 60 * 24); 157 | } catch (ParseException e) { 158 | e.printStackTrace(); 159 | } 160 | return days + 1; 161 | } 162 | 163 | /** 164 | * 比较两个日期是否在一个月内 165 | * @param beginDate 166 | * 开始日期 167 | * @param endDate 168 | * 结束日期 169 | * @return true说明超过一个月 170 | */ 171 | public static boolean compareDateInMonth(String beginDate, String endDate) { 172 | boolean bool = false; 173 | SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd"); 174 | Calendar c = Calendar.getInstance(); 175 | try { 176 | c.setTime(sdf.parse(beginDate)); 177 | int syear = c.get(Calendar.YEAR); 178 | int smonth = c.get(Calendar.MONTH); 179 | int sday = c.get(Calendar.DATE); 180 | c.setTime(sdf.parse(endDate)); 181 | int eyear = c.get(Calendar.YEAR); 182 | int emonth = c.get(Calendar.MONTH); 183 | int eday = c.get(Calendar.DATE); 184 | if (syear != eyear) { 185 | if (eyear - syear == 1) { 186 | if (smonth - emonth == 11 && eday < sday) { 187 | bool = false; 188 | } else { 189 | bool = true; 190 | } 191 | } else { 192 | bool = true; 193 | } 194 | } else { 195 | if (emonth - smonth > 1) { 196 | bool = true; 197 | } else if (emonth - smonth == 1) { 198 | if (eday >= sday) { 199 | bool = true; 200 | } 201 | } 202 | } 203 | } catch (ParseException e) { 204 | e.printStackTrace(); 205 | } 206 | return bool; 207 | } 208 | 209 | /** 210 | * 取到某个日期months个月之后日期 211 | * 212 | * @param date 213 | * 格式yyyyMMdd 214 | * @param months 215 | * 月数 216 | * @return 217 | */ 218 | public static String getDateOfMonth(String date, int months) { 219 | SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd"); 220 | try { 221 | Date toDate = sdf.parse(date); 222 | Calendar c = Calendar.getInstance(); 223 | c.setTime(toDate); 224 | c.add(Calendar.MONTH, months); 225 | date = sdf.format(c.getTime()); 226 | } catch (ParseException e) { 227 | e.printStackTrace(); 228 | } 229 | return date; 230 | } 231 | 232 | /** 233 | * 格式化日期为YYYY年MM月DD日 例如 20130805格式化为2013年08月04日 234 | * @param str 235 | * 格式YYYYMMDD 236 | * @return date 格式YYYY年MM月DD日 237 | */ 238 | public static String formatDateCHN(String str) { 239 | String format = "yyyyMMdd"; 240 | if (str.contains("-")) { 241 | format = "yyyy-MM-dd"; 242 | } 243 | SimpleDateFormat sdf = new SimpleDateFormat(format); 244 | Date date = new Date(); 245 | try { 246 | date = sdf.parse(str); 247 | } catch (ParseException e) { 248 | e.printStackTrace(); 249 | } 250 | SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd"); 251 | return sdf1.format(date); 252 | } 253 | 254 | /** 255 | * 格式化时间为HH:MM:SS,如果数据不为6位,前补0 例如 091516格式化为09:15:16 256 | * @param str 257 | * 格式HHMMSS 258 | * @return time 格式HH:MM:SS 259 | */ 260 | public static String formatTime(String str) { 261 | String format = "HHmmss"; 262 | if (str.length() == 5) { 263 | str = "0" + str; 264 | } 265 | if (str.length() == 9) { 266 | format = "HHmmssSSS"; 267 | } 268 | SimpleDateFormat sdf = new SimpleDateFormat(format); 269 | SimpleDateFormat sdf1 = new SimpleDateFormat("HH:mm:ss"); 270 | try { 271 | str = sdf1.format(sdf.parse(str)); 272 | } catch (ParseException e) { 273 | e.printStackTrace(); 274 | } 275 | return str; 276 | } 277 | 278 | /** 279 | * 计算两个日期相隔天数 280 | * @param smdate 281 | * 小日期 282 | * @param bdate 283 | * 大日期 284 | * @return int 285 | * @throws ParseException 286 | */ 287 | public static int getDaysBetween(String smdate, String bdate) { 288 | SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd"); 289 | Calendar cal = Calendar.getInstance(); 290 | long time1 = 0; 291 | long time2 = 0; 292 | try { 293 | cal.setTime(sdf.parse(smdate)); 294 | time1 = cal.getTimeInMillis(); 295 | cal.setTime(sdf.parse(bdate)); 296 | time2 = cal.getTimeInMillis(); 297 | } catch (ParseException e) { 298 | e.printStackTrace(); 299 | } 300 | long between_days = (time2 - time1) / (1000 * 3600 * 24); 301 | return Integer.parseInt(String.valueOf(between_days)); 302 | } 303 | 304 | /** 305 | * 比较两个日期 306 | * @param startDate 307 | * 开始日期 308 | * @param endDate 309 | * 结束日期 310 | * 结束日期 > 开始日期 返回大于0 311 | * 结束日期 < 开始日期 返回小于0 312 | * @return int 313 | * @throws ParseException 314 | */ 315 | public static long compareDate(String startDate, String endDate) { 316 | SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd"); 317 | Calendar cal = Calendar.getInstance(); 318 | long time1 = 0; 319 | long time2 = 0; 320 | try { 321 | cal.setTime(sdf.parse(startDate)); 322 | time1 = cal.getTimeInMillis(); 323 | cal.setTime(sdf.parse(endDate)); 324 | time2 = cal.getTimeInMillis(); 325 | } catch (ParseException e) { 326 | e.printStackTrace(); 327 | } 328 | return time2 - time1; 329 | } 330 | 331 | /** 332 | * 计算两个日期相隔月数 333 | * @param smdate 334 | * 小日期 335 | * @param bdate 336 | * 大日期 337 | * @return int 338 | * @throws ParseException 339 | */ 340 | public static int getMonthsBetween(String smdate, String bdate) { 341 | SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd"); 342 | Calendar calStart = Calendar.getInstance(); 343 | Calendar calEnd = Calendar.getInstance(); 344 | int compMonth = 0, compYear = 0; 345 | try { 346 | calStart.setTime(sdf.parse(smdate)); 347 | calEnd.setTime(sdf.parse(bdate)); 348 | compMonth = calEnd.get(Calendar.MONTH) 349 | - calStart.get(Calendar.MONTH); 350 | compYear = calEnd.get(Calendar.YEAR) - calStart.get(Calendar.YEAR); 351 | } catch (ParseException e) { 352 | e.printStackTrace(); 353 | } 354 | compMonth = compYear * 12 + compMonth; 355 | return compMonth; 356 | } 357 | 358 | /** 359 | * 格式化日期 传入19900101 返回 1990-01-01 传入为空或null 返回空 360 | * @param str 361 | * @return 362 | */ 363 | public static String formatDate(String str) { 364 | String format = "yyyyMMdd"; 365 | if (null == str) { 366 | return ""; 367 | } else if (str.equals("")) { 368 | return ""; 369 | } else if (str.contains("-")) { 370 | return str; 371 | } else { 372 | SimpleDateFormat sdf = new SimpleDateFormat(format); 373 | Date date = new Date(); 374 | try { 375 | date = sdf.parse(str); 376 | } catch (ParseException e) { 377 | return ""; 378 | } 379 | SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd"); 380 | return sdf1.format(date); 381 | } 382 | } 383 | 384 | /** 385 | * 格式化日期型字符串 386 | * @param date 387 | * @param dateFormatFrom 388 | * @param dateFormatTo 389 | * @return 390 | */ 391 | public static String formatDate(String date, String dateFormatFrom, 392 | String dateFormatTo) { 393 | SimpleDateFormat sdf = new SimpleDateFormat(dateFormatFrom); 394 | Date temp = new Date(); 395 | try { 396 | temp = sdf.parse(date); 397 | } catch (ParseException e) { 398 | return date; 399 | } 400 | SimpleDateFormat sdf1 = new SimpleDateFormat(dateFormatTo); 401 | return sdf1.format(temp); 402 | } 403 | } 404 | -------------------------------------------------------------------------------- /src/main/java/com/xcbeyond/common/StringUtils.java: -------------------------------------------------------------------------------- 1 | package com.xcbeyond.common; 2 | 3 | import org.springframework.lang.Nullable; 4 | import org.springframework.util.Assert; 5 | import org.springframework.util.CollectionUtils; 6 | import org.springframework.util.ObjectUtils; 7 | 8 | import java.io.ByteArrayOutputStream; 9 | import java.nio.charset.Charset; 10 | import java.util.*; 11 | 12 | /** 13 | * 字符串工具类 14 | * @Auther: xcbeyond 15 | * @Date: 2019/1/29 17:04 16 | */ 17 | public class StringUtils { 18 | private static final String FOLDER_SEPARATOR = "/"; 19 | private static final String WINDOWS_FOLDER_SEPARATOR = "\\"; 20 | private static final String TOP_PATH = ".."; 21 | private static final String CURRENT_PATH = "."; 22 | private static final char EXTENSION_SEPARATOR = '.'; 23 | 24 | public StringUtils() { 25 | } 26 | 27 | public static boolean isEmpty(@Nullable Object str) { 28 | return str == null || "".equals(str); 29 | } 30 | 31 | public static boolean isNotEmpty(@Nullable Object str) { 32 | return str != null && !"".equals(str); 33 | } 34 | 35 | public static boolean hasLength(@Nullable CharSequence str) { 36 | return str != null && str.length() > 0; 37 | } 38 | 39 | public static boolean hasLength(@Nullable String str) { 40 | return str != null && !str.isEmpty(); 41 | } 42 | 43 | public static boolean hasText(@Nullable CharSequence str) { 44 | return str != null && str.length() > 0 && containsText(str); 45 | } 46 | 47 | public static boolean hasText(@Nullable String str) { 48 | return str != null && !str.isEmpty() && containsText(str); 49 | } 50 | 51 | private static boolean containsText(CharSequence str) { 52 | int strLen = str.length(); 53 | 54 | for(int i = 0; i < strLen; ++i) { 55 | if (!Character.isWhitespace(str.charAt(i))) { 56 | return true; 57 | } 58 | } 59 | 60 | return false; 61 | } 62 | 63 | public static boolean containsWhitespace(@Nullable CharSequence str) { 64 | if (!hasLength(str)) { 65 | return false; 66 | } else { 67 | int strLen = str.length(); 68 | 69 | for(int i = 0; i < strLen; ++i) { 70 | if (Character.isWhitespace(str.charAt(i))) { 71 | return true; 72 | } 73 | } 74 | 75 | return false; 76 | } 77 | } 78 | 79 | public static boolean containsWhitespace(@Nullable String str) { 80 | return containsWhitespace((CharSequence)str); 81 | } 82 | 83 | public static String trimWhitespace(String str) { 84 | if (!hasLength(str)) { 85 | return str; 86 | } else { 87 | int beginIndex = 0; 88 | 89 | int endIndex; 90 | for(endIndex = str.length() - 1; beginIndex <= endIndex && Character.isWhitespace(str.charAt(beginIndex)); ++beginIndex) { 91 | } 92 | 93 | while(endIndex > beginIndex && Character.isWhitespace(str.charAt(endIndex))) { 94 | --endIndex; 95 | } 96 | 97 | return str.substring(beginIndex, endIndex + 1); 98 | } 99 | } 100 | 101 | public static String trimAllWhitespace(String str) { 102 | if (!hasLength(str)) { 103 | return str; 104 | } else { 105 | int len = str.length(); 106 | StringBuilder sb = new StringBuilder(str.length()); 107 | 108 | for(int i = 0; i < len; ++i) { 109 | char c = str.charAt(i); 110 | if (!Character.isWhitespace(c)) { 111 | sb.append(c); 112 | } 113 | } 114 | 115 | return sb.toString(); 116 | } 117 | } 118 | 119 | public static String trimLeadingWhitespace(String str) { 120 | if (!hasLength(str)) { 121 | return str; 122 | } else { 123 | StringBuilder sb = new StringBuilder(str); 124 | 125 | while(sb.length() > 0 && Character.isWhitespace(sb.charAt(0))) { 126 | sb.deleteCharAt(0); 127 | } 128 | 129 | return sb.toString(); 130 | } 131 | } 132 | 133 | public static String trimTrailingWhitespace(String str) { 134 | if (!hasLength(str)) { 135 | return str; 136 | } else { 137 | StringBuilder sb = new StringBuilder(str); 138 | 139 | while(sb.length() > 0 && Character.isWhitespace(sb.charAt(sb.length() - 1))) { 140 | sb.deleteCharAt(sb.length() - 1); 141 | } 142 | 143 | return sb.toString(); 144 | } 145 | } 146 | 147 | public static String trimLeadingCharacter(String str, char leadingCharacter) { 148 | if (!hasLength(str)) { 149 | return str; 150 | } else { 151 | StringBuilder sb = new StringBuilder(str); 152 | 153 | while(sb.length() > 0 && sb.charAt(0) == leadingCharacter) { 154 | sb.deleteCharAt(0); 155 | } 156 | 157 | return sb.toString(); 158 | } 159 | } 160 | 161 | public static String trimTrailingCharacter(String str, char trailingCharacter) { 162 | if (!hasLength(str)) { 163 | return str; 164 | } else { 165 | StringBuilder sb = new StringBuilder(str); 166 | 167 | while(sb.length() > 0 && sb.charAt(sb.length() - 1) == trailingCharacter) { 168 | sb.deleteCharAt(sb.length() - 1); 169 | } 170 | 171 | return sb.toString(); 172 | } 173 | } 174 | 175 | public static boolean startsWithIgnoreCase(@Nullable String str, @Nullable String prefix) { 176 | return str != null && prefix != null && str.length() >= prefix.length() && str.regionMatches(true, 0, prefix, 0, prefix.length()); 177 | } 178 | 179 | public static boolean endsWithIgnoreCase(@Nullable String str, @Nullable String suffix) { 180 | return str != null && suffix != null && str.length() >= suffix.length() && str.regionMatches(true, str.length() - suffix.length(), suffix, 0, suffix.length()); 181 | } 182 | 183 | public static boolean substringMatch(CharSequence str, int index, CharSequence substring) { 184 | if (index + substring.length() > str.length()) { 185 | return false; 186 | } else { 187 | for(int i = 0; i < substring.length(); ++i) { 188 | if (str.charAt(index + i) != substring.charAt(i)) { 189 | return false; 190 | } 191 | } 192 | 193 | return true; 194 | } 195 | } 196 | 197 | public static int countOccurrencesOf(String str, String sub) { 198 | if (hasLength(str) && hasLength(sub)) { 199 | int count = 0; 200 | 201 | int idx; 202 | for(int pos = 0; (idx = str.indexOf(sub, pos)) != -1; pos = idx + sub.length()) { 203 | ++count; 204 | } 205 | 206 | return count; 207 | } else { 208 | return 0; 209 | } 210 | } 211 | 212 | public static String replace(String inString, String oldPattern, @Nullable String newPattern) { 213 | if (hasLength(inString) && hasLength(oldPattern) && newPattern != null) { 214 | int index = inString.indexOf(oldPattern); 215 | if (index == -1) { 216 | return inString; 217 | } else { 218 | int capacity = inString.length(); 219 | if (newPattern.length() > oldPattern.length()) { 220 | capacity += 16; 221 | } 222 | 223 | StringBuilder sb = new StringBuilder(capacity); 224 | int pos = 0; 225 | 226 | for(int patLen = oldPattern.length(); index >= 0; index = inString.indexOf(oldPattern, pos)) { 227 | sb.append(inString.substring(pos, index)); 228 | sb.append(newPattern); 229 | pos = index + patLen; 230 | } 231 | 232 | sb.append(inString.substring(pos)); 233 | return sb.toString(); 234 | } 235 | } else { 236 | return inString; 237 | } 238 | } 239 | 240 | public static String delete(String inString, String pattern) { 241 | return replace(inString, pattern, ""); 242 | } 243 | 244 | public static String deleteAny(String inString, @Nullable String charsToDelete) { 245 | if (hasLength(inString) && hasLength(charsToDelete)) { 246 | StringBuilder sb = new StringBuilder(inString.length()); 247 | 248 | for(int i = 0; i < inString.length(); ++i) { 249 | char c = inString.charAt(i); 250 | if (charsToDelete.indexOf(c) == -1) { 251 | sb.append(c); 252 | } 253 | } 254 | 255 | return sb.toString(); 256 | } else { 257 | return inString; 258 | } 259 | } 260 | 261 | @Nullable 262 | public static String quote(@Nullable String str) { 263 | return str != null ? "'" + str + "'" : null; 264 | } 265 | 266 | @Nullable 267 | public static Object quoteIfString(@Nullable Object obj) { 268 | return obj instanceof String ? quote((String)obj) : obj; 269 | } 270 | 271 | public static String unqualify(String qualifiedName) { 272 | return unqualify(qualifiedName, '.'); 273 | } 274 | 275 | public static String unqualify(String qualifiedName, char separator) { 276 | return qualifiedName.substring(qualifiedName.lastIndexOf(separator) + 1); 277 | } 278 | 279 | public static String capitalize(String str) { 280 | return changeFirstCharacterCase(str, true); 281 | } 282 | 283 | public static String uncapitalize(String str) { 284 | return changeFirstCharacterCase(str, false); 285 | } 286 | 287 | private static String changeFirstCharacterCase(String str, boolean capitalize) { 288 | if (!hasLength(str)) { 289 | return str; 290 | } else { 291 | char baseChar = str.charAt(0); 292 | char updatedChar; 293 | if (capitalize) { 294 | updatedChar = Character.toUpperCase(baseChar); 295 | } else { 296 | updatedChar = Character.toLowerCase(baseChar); 297 | } 298 | 299 | if (baseChar == updatedChar) { 300 | return str; 301 | } else { 302 | char[] chars = str.toCharArray(); 303 | chars[0] = updatedChar; 304 | return new String(chars, 0, chars.length); 305 | } 306 | } 307 | } 308 | 309 | @Nullable 310 | public static String getFilename(@Nullable String path) { 311 | if (path == null) { 312 | return null; 313 | } else { 314 | int separatorIndex = path.lastIndexOf("/"); 315 | return separatorIndex != -1 ? path.substring(separatorIndex + 1) : path; 316 | } 317 | } 318 | 319 | @Nullable 320 | public static String getFilenameExtension(@Nullable String path) { 321 | if (path == null) { 322 | return null; 323 | } else { 324 | int extIndex = path.lastIndexOf(46); 325 | if (extIndex == -1) { 326 | return null; 327 | } else { 328 | int folderIndex = path.lastIndexOf("/"); 329 | return folderIndex > extIndex ? null : path.substring(extIndex + 1); 330 | } 331 | } 332 | } 333 | 334 | public static String stripFilenameExtension(String path) { 335 | int extIndex = path.lastIndexOf(46); 336 | if (extIndex == -1) { 337 | return path; 338 | } else { 339 | int folderIndex = path.lastIndexOf("/"); 340 | return folderIndex > extIndex ? path : path.substring(0, extIndex); 341 | } 342 | } 343 | 344 | public static String applyRelativePath(String path, String relativePath) { 345 | int separatorIndex = path.lastIndexOf("/"); 346 | if (separatorIndex != -1) { 347 | String newPath = path.substring(0, separatorIndex); 348 | if (!relativePath.startsWith("/")) { 349 | newPath = newPath + "/"; 350 | } 351 | 352 | return newPath + relativePath; 353 | } else { 354 | return relativePath; 355 | } 356 | } 357 | 358 | public static String cleanPath(String path) { 359 | if (!hasLength(path)) { 360 | return path; 361 | } else { 362 | String pathToUse = replace(path, "\\", "/"); 363 | int prefixIndex = pathToUse.indexOf(58); 364 | String prefix = ""; 365 | if (prefixIndex != -1) { 366 | prefix = pathToUse.substring(0, prefixIndex + 1); 367 | if (prefix.contains("/")) { 368 | prefix = ""; 369 | } else { 370 | pathToUse = pathToUse.substring(prefixIndex + 1); 371 | } 372 | } 373 | 374 | if (pathToUse.startsWith("/")) { 375 | prefix = prefix + "/"; 376 | pathToUse = pathToUse.substring(1); 377 | } 378 | 379 | String[] pathArray = delimitedListToStringArray(pathToUse, "/"); 380 | LinkedList pathElements = new LinkedList(); 381 | int tops = 0; 382 | 383 | int i; 384 | for(i = pathArray.length - 1; i >= 0; --i) { 385 | String element = pathArray[i]; 386 | if (!".".equals(element)) { 387 | if ("..".equals(element)) { 388 | ++tops; 389 | } else if (tops > 0) { 390 | --tops; 391 | } else { 392 | pathElements.add(0, element); 393 | } 394 | } 395 | } 396 | 397 | for(i = 0; i < tops; ++i) { 398 | pathElements.add(0, ".."); 399 | } 400 | 401 | if (pathElements.size() == 1 && "".equals(pathElements.getLast()) && !prefix.endsWith("/")) { 402 | pathElements.add(0, "."); 403 | } 404 | 405 | return prefix + collectionToDelimitedString(pathElements, "/"); 406 | } 407 | } 408 | 409 | public static boolean pathEquals(String path1, String path2) { 410 | return cleanPath(path1).equals(cleanPath(path2)); 411 | } 412 | 413 | public static String uriDecode(String source, Charset charset) { 414 | int length = source.length(); 415 | if (length == 0) { 416 | return source; 417 | } else { 418 | Assert.notNull(charset, "Charset must not be null"); 419 | ByteArrayOutputStream bos = new ByteArrayOutputStream(length); 420 | boolean changed = false; 421 | 422 | for(int i = 0; i < length; ++i) { 423 | int ch = source.charAt(i); 424 | if (ch == '%') { 425 | if (i + 2 >= length) { 426 | throw new IllegalArgumentException("Invalid encoded sequence \"" + source.substring(i) + "\""); 427 | } 428 | 429 | char hex1 = source.charAt(i + 1); 430 | char hex2 = source.charAt(i + 2); 431 | int u = Character.digit(hex1, 16); 432 | int l = Character.digit(hex2, 16); 433 | if (u == -1 || l == -1) { 434 | throw new IllegalArgumentException("Invalid encoded sequence \"" + source.substring(i) + "\""); 435 | } 436 | 437 | bos.write((char)((u << 4) + l)); 438 | i += 2; 439 | changed = true; 440 | } else { 441 | bos.write(ch); 442 | } 443 | } 444 | 445 | return changed ? new String(bos.toByteArray(), charset) : source; 446 | } 447 | } 448 | 449 | @Nullable 450 | public static Locale parseLocale(String localeValue) { 451 | String[] tokens = tokenizeLocaleSource(localeValue); 452 | if (tokens.length == 1) { 453 | Locale resolved = Locale.forLanguageTag(localeValue); 454 | return resolved.getLanguage().length() > 0 ? resolved : null; 455 | } else { 456 | return parseLocaleTokens(localeValue, tokens); 457 | } 458 | } 459 | 460 | @Nullable 461 | public static Locale parseLocaleString(String localeString) { 462 | return parseLocaleTokens(localeString, tokenizeLocaleSource(localeString)); 463 | } 464 | 465 | private static String[] tokenizeLocaleSource(String localeSource) { 466 | return tokenizeToStringArray(localeSource, "_ ", false, false); 467 | } 468 | 469 | @Nullable 470 | private static Locale parseLocaleTokens(String localeString, String[] tokens) { 471 | String language = tokens.length > 0 ? tokens[0] : ""; 472 | String country = tokens.length > 1 ? tokens[1] : ""; 473 | validateLocalePart(language); 474 | validateLocalePart(country); 475 | String variant = ""; 476 | if (tokens.length > 2) { 477 | int endIndexOfCountryCode = localeString.indexOf(country, language.length()) + country.length(); 478 | variant = trimLeadingWhitespace(localeString.substring(endIndexOfCountryCode)); 479 | if (variant.startsWith("_")) { 480 | variant = trimLeadingCharacter(variant, '_'); 481 | } 482 | } 483 | 484 | if ("".equals(variant) && country.startsWith("#")) { 485 | variant = country; 486 | country = ""; 487 | } 488 | 489 | return language.length() > 0 ? new Locale(language, country, variant) : null; 490 | } 491 | 492 | private static void validateLocalePart(String localePart) { 493 | for(int i = 0; i < localePart.length(); ++i) { 494 | char ch = localePart.charAt(i); 495 | if (ch != ' ' && ch != '_' && ch != '#' && !Character.isLetterOrDigit(ch)) { 496 | throw new IllegalArgumentException("Locale part \"" + localePart + "\" contains invalid characters"); 497 | } 498 | } 499 | 500 | } 501 | 502 | /** @deprecated */ 503 | @Deprecated 504 | public static String toLanguageTag(Locale locale) { 505 | return locale.getLanguage() + (hasText(locale.getCountry()) ? "-" + locale.getCountry() : ""); 506 | } 507 | 508 | public static TimeZone parseTimeZoneString(String timeZoneString) { 509 | TimeZone timeZone = TimeZone.getTimeZone(timeZoneString); 510 | if ("GMT".equals(timeZone.getID()) && !timeZoneString.startsWith("GMT")) { 511 | throw new IllegalArgumentException("Invalid time zone specification '" + timeZoneString + "'"); 512 | } else { 513 | return timeZone; 514 | } 515 | } 516 | 517 | public static String[] addStringToArray(@Nullable String[] array, String str) { 518 | if (ObjectUtils.isEmpty(array)) { 519 | return new String[]{str}; 520 | } else { 521 | String[] newArr = new String[array.length + 1]; 522 | System.arraycopy(array, 0, newArr, 0, array.length); 523 | newArr[array.length] = str; 524 | return newArr; 525 | } 526 | } 527 | 528 | @Nullable 529 | public static String[] concatenateStringArrays(@Nullable String[] array1, @Nullable String[] array2) { 530 | if (ObjectUtils.isEmpty(array1)) { 531 | return array2; 532 | } else if (ObjectUtils.isEmpty(array2)) { 533 | return array1; 534 | } else { 535 | String[] newArr = new String[array1.length + array2.length]; 536 | System.arraycopy(array1, 0, newArr, 0, array1.length); 537 | System.arraycopy(array2, 0, newArr, array1.length, array2.length); 538 | return newArr; 539 | } 540 | } 541 | 542 | /** @deprecated */ 543 | @Deprecated 544 | @Nullable 545 | public static String[] mergeStringArrays(@Nullable String[] array1, @Nullable String[] array2) { 546 | if (ObjectUtils.isEmpty(array1)) { 547 | return array2; 548 | } else if (ObjectUtils.isEmpty(array2)) { 549 | return array1; 550 | } else { 551 | List result = new ArrayList(); 552 | result.addAll(Arrays.asList(array1)); 553 | String[] var3 = array2; 554 | int var4 = array2.length; 555 | 556 | for(int var5 = 0; var5 < var4; ++var5) { 557 | String str = var3[var5]; 558 | if (!result.contains(str)) { 559 | result.add(str); 560 | } 561 | } 562 | 563 | return toStringArray((Collection)result); 564 | } 565 | } 566 | 567 | public static String[] sortStringArray(String[] array) { 568 | if (ObjectUtils.isEmpty(array)) { 569 | return new String[0]; 570 | } else { 571 | Arrays.sort(array); 572 | return array; 573 | } 574 | } 575 | 576 | public static String[] toStringArray(Collection collection) { 577 | return (String[])collection.toArray(new String[0]); 578 | } 579 | 580 | public static String[] toStringArray(Enumeration enumeration) { 581 | return toStringArray((Collection)Collections.list(enumeration)); 582 | } 583 | 584 | public static String[] trimArrayElements(@Nullable String[] array) { 585 | if (ObjectUtils.isEmpty(array)) { 586 | return new String[0]; 587 | } else { 588 | String[] result = new String[array.length]; 589 | 590 | for(int i = 0; i < array.length; ++i) { 591 | String element = array[i]; 592 | result[i] = element != null ? element.trim() : null; 593 | } 594 | 595 | return result; 596 | } 597 | } 598 | 599 | public static String[] removeDuplicateStrings(String[] array) { 600 | if (ObjectUtils.isEmpty(array)) { 601 | return array; 602 | } else { 603 | Set set = new LinkedHashSet(Arrays.asList(array)); 604 | return toStringArray((Collection)set); 605 | } 606 | } 607 | 608 | @Nullable 609 | public static String[] split(@Nullable String toSplit, @Nullable String delimiter) { 610 | if (hasLength(toSplit) && hasLength(delimiter)) { 611 | int offset = toSplit.indexOf(delimiter); 612 | if (offset < 0) { 613 | return null; 614 | } else { 615 | String beforeDelimiter = toSplit.substring(0, offset); 616 | String afterDelimiter = toSplit.substring(offset + delimiter.length()); 617 | return new String[]{beforeDelimiter, afterDelimiter}; 618 | } 619 | } else { 620 | return null; 621 | } 622 | } 623 | 624 | @Nullable 625 | public static Properties splitArrayElementsIntoProperties(String[] array, String delimiter) { 626 | return splitArrayElementsIntoProperties(array, delimiter, (String)null); 627 | } 628 | 629 | @Nullable 630 | public static Properties splitArrayElementsIntoProperties(String[] array, String delimiter, @Nullable String charsToDelete) { 631 | if (ObjectUtils.isEmpty(array)) { 632 | return null; 633 | } else { 634 | Properties result = new Properties(); 635 | String[] var4 = array; 636 | int var5 = array.length; 637 | 638 | for(int var6 = 0; var6 < var5; ++var6) { 639 | String element = var4[var6]; 640 | if (charsToDelete != null) { 641 | element = deleteAny(element, charsToDelete); 642 | } 643 | 644 | String[] splittedElement = split(element, delimiter); 645 | if (splittedElement != null) { 646 | result.setProperty(splittedElement[0].trim(), splittedElement[1].trim()); 647 | } 648 | } 649 | 650 | return result; 651 | } 652 | } 653 | 654 | public static String[] tokenizeToStringArray(@Nullable String str, String delimiters) { 655 | return tokenizeToStringArray(str, delimiters, true, true); 656 | } 657 | 658 | public static String[] tokenizeToStringArray(@Nullable String str, String delimiters, boolean trimTokens, boolean ignoreEmptyTokens) { 659 | if (str == null) { 660 | return new String[0]; 661 | } else { 662 | StringTokenizer st = new StringTokenizer(str, delimiters); 663 | ArrayList tokens = new ArrayList(); 664 | 665 | while(true) { 666 | String token; 667 | do { 668 | if (!st.hasMoreTokens()) { 669 | return toStringArray((Collection)tokens); 670 | } 671 | 672 | token = st.nextToken(); 673 | if (trimTokens) { 674 | token = token.trim(); 675 | } 676 | } while(ignoreEmptyTokens && token.length() <= 0); 677 | 678 | tokens.add(token); 679 | } 680 | } 681 | } 682 | 683 | public static String[] delimitedListToStringArray(@Nullable String str, @Nullable String delimiter) { 684 | return delimitedListToStringArray(str, delimiter, (String)null); 685 | } 686 | 687 | public static String[] delimitedListToStringArray(@Nullable String str, @Nullable String delimiter, @Nullable String charsToDelete) { 688 | if (str == null) { 689 | return new String[0]; 690 | } else if (delimiter == null) { 691 | return new String[]{str}; 692 | } else { 693 | List result = new ArrayList(); 694 | int pos; 695 | if ("".equals(delimiter)) { 696 | for(pos = 0; pos < str.length(); ++pos) { 697 | result.add(deleteAny(str.substring(pos, pos + 1), charsToDelete)); 698 | } 699 | } else { 700 | int delPos; 701 | for(pos = 0; (delPos = str.indexOf(delimiter, pos)) != -1; pos = delPos + delimiter.length()) { 702 | result.add(deleteAny(str.substring(pos, delPos), charsToDelete)); 703 | } 704 | 705 | if (str.length() > 0 && pos <= str.length()) { 706 | result.add(deleteAny(str.substring(pos), charsToDelete)); 707 | } 708 | } 709 | 710 | return toStringArray((Collection)result); 711 | } 712 | } 713 | 714 | public static String[] commaDelimitedListToStringArray(@Nullable String str) { 715 | return delimitedListToStringArray(str, ","); 716 | } 717 | 718 | public static Set commaDelimitedListToSet(@Nullable String str) { 719 | String[] tokens = commaDelimitedListToStringArray(str); 720 | return new LinkedHashSet(Arrays.asList(tokens)); 721 | } 722 | 723 | public static String collectionToDelimitedString(@Nullable Collection coll, String delim, String prefix, String suffix) { 724 | if (CollectionUtils.isEmpty(coll)) { 725 | return ""; 726 | } else { 727 | StringBuilder sb = new StringBuilder(); 728 | Iterator it = coll.iterator(); 729 | 730 | while(it.hasNext()) { 731 | sb.append(prefix).append(it.next()).append(suffix); 732 | if (it.hasNext()) { 733 | sb.append(delim); 734 | } 735 | } 736 | 737 | return sb.toString(); 738 | } 739 | } 740 | 741 | public static String collectionToDelimitedString(@Nullable Collection coll, String delim) { 742 | return collectionToDelimitedString(coll, delim, "", ""); 743 | } 744 | 745 | public static String collectionToCommaDelimitedString(@Nullable Collection coll) { 746 | return collectionToDelimitedString(coll, ","); 747 | } 748 | 749 | public static String arrayToDelimitedString(@Nullable Object[] arr, String delim) { 750 | if (ObjectUtils.isEmpty(arr)) { 751 | return ""; 752 | } else if (arr.length == 1) { 753 | return ObjectUtils.nullSafeToString(arr[0]); 754 | } else { 755 | StringBuilder sb = new StringBuilder(); 756 | 757 | for(int i = 0; i < arr.length; ++i) { 758 | if (i > 0) { 759 | sb.append(delim); 760 | } 761 | 762 | sb.append(arr[i]); 763 | } 764 | 765 | return sb.toString(); 766 | } 767 | } 768 | 769 | public static String arrayToCommaDelimitedString(@Nullable Object[] arr) { 770 | return arrayToDelimitedString(arr, ","); 771 | } 772 | } 773 | -------------------------------------------------------------------------------- /src/main/java/com/xcbeyond/common/communication/http/HttpClient.java: -------------------------------------------------------------------------------- 1 | package com.xcbeyond.common.communication.http; 2 | 3 | import org.jsoup.Connection; 4 | import org.jsoup.Connection.Response; 5 | import org.jsoup.Jsoup; 6 | 7 | import javax.net.ssl.*; 8 | import java.io.*; 9 | import java.net.URLEncoder; 10 | import java.security.SecureRandom; 11 | import java.security.cert.CertificateException; 12 | import java.security.cert.X509Certificate; 13 | import java.util.ArrayList; 14 | import java.util.List; 15 | import java.util.Map; 16 | import java.util.Map.Entry; 17 | import java.util.Set; 18 | 19 | /** 20 | * Http、Https客户端 21 | * @Auther: xcbeyond 22 | * @Date: 2019/1/17 18:03 23 | */ 24 | public class HttpClient { 25 | 26 | /** 27 | * 请求超时时间 28 | */ 29 | private static final int TIME_OUT = 120000; 30 | 31 | /** 32 | * Https请求 33 | */ 34 | private static final String HTTPS = "https"; 35 | 36 | /** 37 | * 发送JSON格式参数POST请求 38 | * @param url 请求路径 39 | * @param params JSON格式请求参数 40 | * @return 服务器响应对象 41 | * @throws IOException 42 | */ 43 | public static Response post(String url, String params) throws IOException { 44 | return doPostRequest(url, null, null, params); 45 | } 46 | 47 | /** 48 | * 字符串参数post请求 49 | * @param url 请求URL地址 50 | * @param param 请求参数Map 51 | * @return 服务器响应对象 52 | * @throws IOException 53 | */ 54 | public static Response post(String url, Map param) throws IOException { 55 | return doPostRequest(url, param, null, null); 56 | } 57 | 58 | /** 59 | * 带上传文件的post请求 60 | * @param url 请求URL地址 61 | * @param param 请求字符串参数集合 62 | * @param fileMap 请求文件参数集合 63 | * @return 服务器响应对象 64 | * @throws IOException 65 | */ 66 | public static Response post(String url, Map param, Map fileMap) throws IOException { 67 | return doPostRequest(url, param, fileMap, null); 68 | } 69 | 70 | /** 71 | * 执行post请求 72 | * @param url 请求URL地址 73 | * @param paramMap 请求字符串参数Map 74 | * @param fileMap 请求文件参数集合 75 | * @return 服务器响应对象 76 | * @throws IOException 77 | */ 78 | private static Response doPostRequest(String url, Map paramMap, Map fileMap, String jsonParams) throws IOException { 79 | if (null == url || url.isEmpty()) { 80 | throw new RuntimeException("The request URL is blank."); 81 | } 82 | 83 | Connection connection = initConn(url, Connection.Method.POST); 84 | 85 | // 收集上传文件输入流,最终全部关闭 86 | List inputStreamList = null; 87 | try { 88 | // 添加文件参数 89 | if (null != fileMap && !fileMap.isEmpty()) { 90 | inputStreamList = new ArrayList(); 91 | InputStream in = null; 92 | File file = null; 93 | Set> set = fileMap.entrySet(); 94 | for (Entry e : set) { 95 | file = e.getValue(); 96 | in = new FileInputStream(file); 97 | inputStreamList.add(in); 98 | connection.data(e.getKey(), file.getName(), in); 99 | } 100 | } 101 | 102 | // 设置请求体为JSON格式内容 103 | else if (null != jsonParams && !jsonParams.isEmpty()) { 104 | connection.header("Content-Type", "application/json;charset=UTF-8"); 105 | connection.requestBody(jsonParams); 106 | } 107 | 108 | // 普通表单提交方式 109 | else { 110 | connection.header("Content-Type", "application/x-www-form-urlencoded"); 111 | } 112 | 113 | // 添加字符串类参数 114 | if (null != paramMap && !paramMap.isEmpty()) { 115 | connection.data(paramMap); 116 | } 117 | 118 | Response response = connection.execute(); 119 | return response; 120 | } catch (FileNotFoundException e) { 121 | throw e; 122 | } catch (IOException e) { 123 | throw e; 124 | } finally {// 关闭上传文件的输入流 125 | if (null != inputStreamList) { 126 | for (InputStream in : inputStreamList) { 127 | try { 128 | in.close(); 129 | } catch (IOException e) { 130 | e.printStackTrace(); 131 | } 132 | } 133 | } 134 | } 135 | } 136 | 137 | /** 138 | * Get请求 139 | * @param url 请求URL 140 | * @param param 请求参数Map 141 | * @return 服务器响应对象 142 | * @throws IOException 143 | */ 144 | public static Response get(String url, Map param) throws IOException { 145 | if (null == url || url.isEmpty()) { 146 | throw new RuntimeException("The request URL is blank."); 147 | } 148 | 149 | if (null != param && !param.isEmpty()) { 150 | StringBuilder sb = new StringBuilder(url); 151 | if (url.indexOf("?") == -1) { 152 | sb.append("?"); 153 | } 154 | sb.append(map2UrlParam(param)); 155 | 156 | url = sb.toString(); 157 | } 158 | 159 | Connection connection = initConn(url, Connection.Method.GET); 160 | Response response = connection.execute(); 161 | 162 | return response; 163 | } 164 | 165 | /** 166 | * Put请求 167 | * @param url 请求URL 168 | * @param param 请求参数Map 169 | * @return 服务器响应对象 170 | * @throws Exception 171 | */ 172 | public static Response put(String url, Map param) throws Exception { 173 | if (null == url || url.isEmpty()) { 174 | throw new RuntimeException("The request URL is blank."); 175 | } 176 | 177 | Connection connection = initConn(url, Connection.Method.PUT); 178 | connection.data(param); 179 | 180 | Response response = connection.execute(); 181 | return response; 182 | } 183 | 184 | /** 185 | * 将请求参数map转换为Url参数字符串 186 | * @param param 请求参数map 187 | * @return Url参数字符串 格式为:key1=value1&key2=value2 188 | * @throws UnsupportedEncodingException 189 | */ 190 | private static String map2UrlParam(Map param) throws UnsupportedEncodingException { 191 | if (null == param || param.isEmpty()) { 192 | return null; 193 | } 194 | StringBuffer url = new StringBuffer(); 195 | boolean isfist = true; 196 | for (Entry entry : param.entrySet()) { 197 | if (isfist) { 198 | isfist = false; 199 | } else { 200 | url.append("&"); 201 | } 202 | url.append(entry.getKey()).append("="); 203 | String value = entry.getValue(); 204 | if (null != value && !"".equals(value)) { 205 | url.append(URLEncoder.encode(value, "UTF-8")); 206 | } 207 | } 208 | return url.toString(); 209 | 210 | } 211 | 212 | /** 213 | * 初始化请求连接 214 | * @param url 215 | * @param requestMethod 216 | * @return 217 | */ 218 | private static Connection initConn(String url, Connection.Method requestMethod) { 219 | // 如果是Https请求 220 | if (url.startsWith(HTTPS)) { 221 | getTrust(); 222 | } 223 | 224 | Connection connection = Jsoup.connect(url); 225 | connection.method(requestMethod); 226 | connection.timeout(TIME_OUT); 227 | connection.ignoreHttpErrors(true); 228 | connection.ignoreContentType(true); 229 | 230 | return connection; 231 | } 232 | 233 | /** 234 | * 获取服务器信任 235 | */ 236 | private static void getTrust() { 237 | try { 238 | HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() { 239 | 240 | public boolean verify(String hostname, SSLSession session) { 241 | return true; 242 | } 243 | }); 244 | SSLContext context = SSLContext.getInstance("TLS"); 245 | context.init(null, new X509TrustManager[] { new X509TrustManager() { 246 | 247 | public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { 248 | } 249 | 250 | public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { 251 | } 252 | 253 | public X509Certificate[] getAcceptedIssuers() { 254 | return new X509Certificate[0]; 255 | } 256 | } }, new SecureRandom()); 257 | HttpsURLConnection.setDefaultSSLSocketFactory(context.getSocketFactory()); 258 | } catch (Exception e) { 259 | e.printStackTrace(); 260 | } 261 | } 262 | } -------------------------------------------------------------------------------- /src/main/java/com/xcbeyond/common/communication/socket/SocketClient.java: -------------------------------------------------------------------------------- 1 | package com.xcbeyond.common.communication.socket; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import java.io.BufferedInputStream; 7 | import java.io.IOException; 8 | import java.io.InputStream; 9 | import java.io.OutputStream; 10 | import java.net.ConnectException; 11 | import java.net.Socket; 12 | import java.net.UnknownHostException; 13 | import java.nio.ByteBuffer; 14 | import java.nio.channels.UnresolvedAddressException; 15 | import java.nio.charset.Charset; 16 | 17 | /** 18 | * socket客户端 19 | * @Auther: xcbeyond 20 | * @Date: 2019/4/22 15:45 21 | */ 22 | public class SocketClient { 23 | // 日志记录类 24 | private static Logger logger = LoggerFactory.getLogger(SocketClient.class); 25 | 26 | // 字符集编码 27 | private static final Charset UTF8 = Charset.forName("UTF-8"); 28 | 29 | // Socket链接地址 30 | private String ip = null; 31 | // Socket链接端口 32 | private int port = 0; 33 | // 消息长度 34 | private int msglen = 8; 35 | // Socket链接超时时间(默认值为30秒) 36 | private int timeout = 30000; 37 | // 读取缓冲区(默认1024字节) 38 | private int bufferCapacity = 1024; 39 | // 字符集 40 | private String charset = "UTF-8"; 41 | 42 | 43 | public SocketClient(String ip, int port) { 44 | this.ip = ip; 45 | this.port = port; 46 | } 47 | 48 | public String sendMessage(String msg) { 49 | logger.debug("通讯开始处理请求!"); 50 | 51 | Socket client = null; 52 | //输入流 53 | InputStream is = null; 54 | //输出流 55 | OutputStream os = null; 56 | //返回报文 57 | byte[] result = null; 58 | try { 59 | // 创建Socket链接 60 | client = new Socket(ip, port); 61 | 62 | // 定义用来存储发送数据的byte缓冲区 63 | byte[] sendContent = msg.getBytes(charset); 64 | int contentLength = sendContent.length; 65 | ByteBuffer sendbuffer = ByteBuffer.allocate(contentLength + 8); 66 | 67 | // 添加报文头 68 | String header = getWriteHead(contentLength); 69 | sendbuffer.put(header.getBytes(charset)); 70 | 71 | sendbuffer.put(msg.getBytes(charset)); 72 | 73 | // 将缓冲区各标志复位,因为向里面put了数据标志被改变要想从中读取数据发向服务器,就要复位 74 | sendbuffer.flip(); 75 | 76 | // 向服务器发送数据 77 | this.sendMsg(sendbuffer.array(), client, os); 78 | // 读取对方返回数据 79 | result = this.readMsg(client, is); 80 | 81 | if (null == result) { 82 | logger.error("收到应答报文为空"); 83 | } 84 | 85 | } catch (UnresolvedAddressException ue) { 86 | logger.error("远程服务配置的地址格式不正确:[" + ip + "]", ue); 87 | } catch (UnknownHostException e) { 88 | logger.error("无法建立到远程服务器地址的链接[" + ip + ":" + port 89 | + "]", e); 90 | } catch (ConnectException ce) { 91 | logger.error("无法连接到远程服务器[" + ip + ":" + port 92 | + "]", ce); 93 | } catch (IOException e) { 94 | logger.error("通讯接出捕获IO错误", e); 95 | } catch (Exception e) { 96 | logger.error("连接到远程服务器执行服务时发生未知异常", e); 97 | } finally { 98 | try { 99 | // 关闭输出流 100 | if (null != os) { 101 | os.close(); 102 | } 103 | // 关闭输入流 104 | if (null != is) { 105 | is.close(); 106 | } 107 | // 关闭链接 108 | if (null != client) { 109 | client.close(); 110 | } 111 | } catch (IOException e) { 112 | logger.error("关闭链接时捕获IO错误,抛出通讯接出异常!", e); 113 | } 114 | } 115 | 116 | return new String(result,UTF8); 117 | } 118 | 119 | 120 | /** 121 | * 读取返回数据 122 | * @param client 123 | * @param is 124 | * @return 125 | * @throws IOException 126 | */ 127 | private byte[] readMsg(Socket client, InputStream is) throws IOException { 128 | // 记录等待起始时间 129 | long startTime = System.currentTimeMillis(); 130 | 131 | //设置超时时间 132 | client.setSoTimeout(timeout); 133 | is = client.getInputStream(); 134 | BufferedInputStream bis = new BufferedInputStream(is); 135 | //首先读取报文头长度 136 | byte[] head = new byte[msglen]; 137 | bis.read(head, 0, head.length); 138 | int length = 0; 139 | try { 140 | length = Integer.parseInt(new String(head)); 141 | } catch (NumberFormatException nfe) { 142 | logger.error("报文格式有误,[报文长度]域包含无效数字!"); 143 | nfe.printStackTrace(); 144 | } 145 | 146 | logger.debug("读取报文头,获得报文内容的大小为:[" + length + "]"); 147 | 148 | //定义用于存放整个报文体的缓冲区数组 149 | byte[] request = new byte[length]; 150 | //已读取到的数据 151 | int bufferSize = 0; 152 | // 每次读取的数据的值 153 | int numberRead = 0; 154 | while (true) { 155 | if(bufferCapacity + bufferSize > length) { 156 | numberRead = bis.read(request, bufferSize, length - bufferSize); 157 | } else { 158 | numberRead = bis.read(request, bufferSize ,bufferCapacity); 159 | } 160 | bufferSize += numberRead; 161 | if (bufferSize == length) { 162 | break; 163 | } 164 | } 165 | 166 | logger.debug("读取报文完成,获取报文内容为:[" + new String(request,UTF8) + "]"); 167 | 168 | // 记录当前时间并计算已等待时间 169 | long currentTime = System.currentTimeMillis(); 170 | long waitTime = currentTime - startTime; 171 | 172 | logger.debug("当前时间:" + currentTime 173 | + ",开始等待读的时间:" + startTime + ",已等待时间:" 174 | + waitTime + "毫秒"); 175 | return request; 176 | } 177 | 178 | /** 179 | * 发送消息 180 | * @param buff 181 | * @return 182 | * @throws IOException 183 | */ 184 | private void sendMsg(byte[] buff, Socket client, OutputStream os) throws IOException { 185 | logger.debug("开始发送数据...\r\n"+ new String(buff, UTF8)); 186 | 187 | os = client.getOutputStream(); 188 | os.write(buff); 189 | os.flush(); 190 | 191 | logger.debug("数据发送完毕,发送数据的长度为:[" + buff.length + "]"); 192 | } 193 | 194 | /** 195 | * 拼发送8位报文长度 196 | * @param length 197 | * @return 198 | */ 199 | private String getWriteHead(long length) { 200 | StringBuilder result = new StringBuilder(); 201 | String res = String.valueOf(length); 202 | if (res.length() == 8) { 203 | result.append(res); 204 | } else { 205 | res = "00000000" + res; 206 | int index = res.length() - 8; 207 | result.append(res.substring(index, res.length())); 208 | } 209 | return result.toString(); 210 | } 211 | } -------------------------------------------------------------------------------- /src/main/java/com/xcbeyond/common/communication/socket/SocketServer.java: -------------------------------------------------------------------------------- 1 | package com.xcbeyond.common.communication.socket; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import java.io.DataInputStream; 7 | import java.io.DataOutputStream; 8 | import java.io.IOException; 9 | import java.net.ServerSocket; 10 | import java.net.Socket; 11 | import java.nio.charset.Charset; 12 | 13 | /** 14 | * socket服务器 15 | * @Auther: xcbeyond 16 | * @Date: 2019/4/22 16:29 17 | */ 18 | public class SocketServer { 19 | private static Logger log = LoggerFactory.getLogger(SocketServer.class); 20 | private static int port = 9999; 21 | 22 | public SocketServer(int port) { 23 | this.port = port; 24 | } 25 | 26 | public void start() { 27 | log.info("开始启动SocketServer,端口:" + port); 28 | try { 29 | ServerSocket server = new ServerSocket(port); 30 | while (true) { 31 | Socket socket = server.accept(); 32 | new Task(socket).start(); 33 | } 34 | } catch (IOException e) { 35 | e.printStackTrace(); 36 | log.error("SocketServer启动失败,异常为:" + e.getMessage()); 37 | } 38 | } 39 | 40 | /** 41 | * 用来处理Socket的Task 42 | */ 43 | class Task extends Thread { 44 | // 字符集编码 45 | private final Charset UTF8 = Charset.forName("UTF-8"); 46 | 47 | private Socket socket; 48 | 49 | public Task(Socket socket) { 50 | this.socket = socket; 51 | } 52 | 53 | @Override 54 | public void run() { 55 | handleSocket(); 56 | 57 | } 58 | 59 | // 完成与客户端socket的通信 60 | private void handleSocket() { 61 | DataInputStream dis = null; 62 | DataOutputStream dos = null; 63 | try { 64 | 65 | dis = new DataInputStream(socket.getInputStream()); 66 | dos = new DataOutputStream(socket.getOutputStream()); 67 | 68 | byte[] contentLengthBy = new byte[8]; 69 | dis.read(contentLengthBy); 70 | int contentLength = Integer.parseInt(new String(contentLengthBy, UTF8)); 71 | byte[] bytes = new byte[contentLength]; 72 | dis.read(bytes); // size是读取到的字节数 73 | 74 | String request = new String(bytes, UTF8); 75 | log.debug("接收到的数据:" + request); 76 | 77 | 78 | //TODO 此处进行服务端的业务逻辑处理 79 | String responseBody = "Request received!"; 80 | 81 | //拼装响应数据长度 82 | String response = getWriteHead(responseBody.getBytes("UTF-8").length) + responseBody; 83 | 84 | log.debug("返回的数据:" + response); 85 | dos.write(response.getBytes(UTF8)); 86 | dos.flush(); 87 | } catch (IOException e) { 88 | e.printStackTrace(); 89 | } finally { 90 | try { 91 | dis.close(); 92 | dos.close(); 93 | socket.close(); 94 | } catch (IOException e) { 95 | e.printStackTrace(); 96 | } 97 | 98 | } 99 | } 100 | } 101 | 102 | /** 103 | * 拼发送报文长度 104 | * @param length 105 | * @return 106 | */ 107 | private String getWriteHead(long length) { 108 | StringBuilder result = new StringBuilder(); 109 | String res = String.valueOf(length); 110 | if (res.length() == 8) { 111 | result.append(res); 112 | } else { 113 | res = "00000000" + res; 114 | int index = res.length() - 8; 115 | result.append(res.substring(index, res.length())); 116 | } 117 | return result.toString(); 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/main/java/com/xcbeyond/common/data/redis/RedisUtils.java: -------------------------------------------------------------------------------- 1 | package com.xcbeyond.common.data.redis; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.data.redis.core.ListOperations; 5 | import org.springframework.data.redis.core.RedisTemplate; 6 | import org.springframework.data.redis.support.atomic.RedisAtomicLong; 7 | import org.springframework.stereotype.Component; 8 | 9 | import java.util.List; 10 | import java.util.Map; 11 | import java.util.Set; 12 | import java.util.concurrent.TimeUnit; 13 | 14 | /** 15 | * redis操作工具类. 16 | * (基于RedisTemplate) 17 | * @Auther: xcbeyond 18 | * @Date: 2019/1/16 16:13 19 | */ 20 | @Component 21 | public class RedisUtils { 22 | @Autowired 23 | private RedisTemplate redisTemplate; 24 | 25 | /** 26 | * 读取缓存 27 | * @param key 28 | * @return 29 | */ 30 | public String get(final String key) { 31 | return redisTemplate.opsForValue().get(key); 32 | } 33 | 34 | /** 35 | * 获取所有以prefix开头的key 36 | * @param prefix 前缀key 37 | * @return 38 | */ 39 | public Set getKeysByPrefix(String prefix){ 40 | return redisTemplate.keys(prefix + "*"); 41 | } 42 | 43 | /** 44 | * 写入缓存 45 | * @param key 46 | * @param value 47 | * @return 48 | */ 49 | public boolean set(final String key, String value) { 50 | boolean result = false; 51 | try { 52 | redisTemplate.opsForValue().set(key, value); 53 | result = true; 54 | } catch (Exception e) { 55 | e.printStackTrace(); 56 | } 57 | return result; 58 | } 59 | 60 | /** 61 | * 写入缓存并设置缓存过期时间 62 | * @param key 63 | * @param value 64 | * @param expiration 过期时间,秒 65 | * @return 66 | */ 67 | public boolean set(final String key, String value,long expiration) { 68 | boolean result = false; 69 | try { 70 | redisTemplate.opsForValue().set(key, value, expiration, TimeUnit.SECONDS); 71 | result = true; 72 | } catch (Exception e) { 73 | e.printStackTrace(); 74 | } 75 | return result; 76 | } 77 | 78 | /** 79 | * 写入缓存,并带自动序列的key 80 | * @param key 81 | * @param value 82 | * @return 83 | */ 84 | public boolean setAutoKey(final String key, String value) { 85 | boolean result = false; 86 | try { 87 | RedisAtomicLong entityIdCounter = new RedisAtomicLong(key, redisTemplate.getConnectionFactory()); 88 | Long increment = entityIdCounter.getAndIncrement(); 89 | 90 | redisTemplate.opsForValue().set(key + ":" + increment, value); 91 | result = true; 92 | } catch (Exception e) { 93 | e.printStackTrace(); 94 | } 95 | return result; 96 | } 97 | 98 | /** 99 | * 是否包含key 100 | * @param key 101 | * @return 102 | */ 103 | public boolean hasKey(final String key) { 104 | boolean result = false; 105 | try { 106 | return redisTemplate.hasKey(key); 107 | } catch (Exception e) { 108 | e.printStackTrace(); 109 | } 110 | return result; 111 | } 112 | 113 | /** 114 | * 更新缓存 115 | * @param key 116 | * @param value 117 | * @return 118 | */ 119 | public boolean getAndSet(final String key, String value) { 120 | boolean result = false; 121 | try { 122 | redisTemplate.opsForValue().getAndSet(key, value); 123 | result = true; 124 | } catch (Exception e) { 125 | e.printStackTrace(); 126 | } 127 | return result; 128 | } 129 | 130 | /** 131 | * 删除缓存 132 | * @param key 133 | * @return 134 | */ 135 | public boolean delete(final String key) { 136 | boolean result = false; 137 | try { 138 | redisTemplate.delete(key); 139 | result = true; 140 | } catch (Exception e) { 141 | e.printStackTrace(); 142 | } 143 | return result; 144 | } 145 | 146 | /** 147 | * 按照key的前缀删除 148 | * @param prefix 前缀key 149 | * @return 150 | */ 151 | public void deleteByPrefix(final String prefix) { 152 | Set keys = redisTemplate.keys(prefix + "*"); 153 | if (!keys.isEmpty()) { 154 | redisTemplate.delete(keys); 155 | } 156 | } 157 | 158 | /** 159 | * 写入 list 以元素的形式 160 | * @param key 161 | * @param value 162 | * @return 163 | */ 164 | public boolean addList(final String key, String value) { 165 | boolean result = false; 166 | try { 167 | ListOperations list = redisTemplate.opsForList(); 168 | list.rightPush(key, value); 169 | result = true; 170 | } catch (Exception e) { 171 | e.printStackTrace(); 172 | } 173 | return result; 174 | } 175 | 176 | /** 177 | * 直接写入 list 178 | * @param key 179 | * @param list 180 | * @return 181 | */ 182 | public boolean addListAll(final String key, List list) { 183 | boolean result = false; 184 | try { 185 | ListOperations list2 = redisTemplate.opsForList(); 186 | list2.rightPushAll(key, list); 187 | result = true; 188 | } catch (Exception e) { 189 | e.printStackTrace(); 190 | } 191 | return result; 192 | } 193 | 194 | /** 195 | * 删除list中元素 196 | * @param key 197 | * @param value 198 | * @return 199 | */ 200 | public boolean deleteListEm(final String key, String value) { 201 | boolean result = false; 202 | try { 203 | ListOperations list2 = redisTemplate.opsForList(); 204 | list2.remove(key, 1, value); //将删除list中存储在列表中第一次出现的 value 205 | result = true; 206 | } catch (Exception e) { 207 | e.printStackTrace(); 208 | } 209 | return result; 210 | } 211 | 212 | /** 213 | * 判断是否存在key 214 | * @param key 215 | * @return 216 | */ 217 | public boolean isExsitKey(final String key){ 218 | return redisTemplate.hasKey(key); 219 | } 220 | 221 | /** 222 | * 增加hash结构的数据 223 | * @param key 224 | * @param map 225 | * @return 226 | */ 227 | public boolean addHash(final String key, Map map){ 228 | boolean result = false; 229 | try { 230 | redisTemplate.opsForHash().putAll(key, map); 231 | result = true; 232 | } catch (Exception e) { 233 | e.printStackTrace(); 234 | } 235 | return result; 236 | } 237 | 238 | /** 239 | * 删除Hash结构的数据 240 | * @param key 241 | * @param mapKey 242 | * @return 243 | */ 244 | public boolean delHash(final String key, String mapKey){ 245 | boolean result = false; 246 | try { 247 | redisTemplate.opsForHash().delete(key, mapKey); 248 | result = true; 249 | } catch (Exception e) { 250 | e.printStackTrace(); 251 | } 252 | return result; 253 | } 254 | 255 | /** 256 | * 获取hash中的值 257 | * @param key 258 | */ 259 | public Map getHashSet(final String key) { 260 | return redisTemplate.opsForHash().entries(key); 261 | } 262 | } 263 | -------------------------------------------------------------------------------- /src/main/java/com/xcbeyond/common/file/chunk/controller/FileController.java: -------------------------------------------------------------------------------- 1 | package com.xcbeyond.common.file.chunk.controller; 2 | 3 | import com.xcbeyond.common.file.chunk.service.FileService; 4 | import org.springframework.web.bind.annotation.*; 5 | import org.springframework.web.multipart.MultipartFile; 6 | 7 | import javax.annotation.Resource; 8 | import javax.servlet.http.HttpServletRequest; 9 | import javax.servlet.http.HttpServletResponse; 10 | 11 | /** 12 | * 文件分片操作Controller 13 | * @Auther: xcbeyond 14 | * @Date: 2019/5/9 22:56 15 | */ 16 | @RestController 17 | @RequestMapping("/file") 18 | public class FileController { 19 | @Resource 20 | private FileService fileService; 21 | 22 | /** 23 | * 文件分片上传 24 | * @param chunkId 分片ID 25 | * @param multipartFile 分片文件 26 | */ 27 | @RequestMapping(value = "/chunk/upload", method = RequestMethod.POST) 28 | public void fileChunkUpload(@RequestParam("chunkId") long chunkId, 29 | @RequestParam(value = "chunk") MultipartFile multipartFile) { 30 | fileService.fileChunkUpload(chunkId, multipartFile); 31 | } 32 | 33 | /** 34 | * 文件分片下载 35 | * @param range http请求头Range,用于表示请求指定部分的内容。 36 | * 格式为:Range: bytes=start-end [start,end]表示,即是包含请求头的start及end字节的内容 37 | * @param request http请求 38 | * @param response http响应 39 | */ 40 | @RequestMapping(value = "/chunk/download", method = RequestMethod.GET) 41 | public void fileChunkDownload(@RequestHeader(value = "Range") String range, 42 | HttpServletRequest request, HttpServletResponse response) { 43 | fileService.fileChunkDownload(range,request,response); 44 | } 45 | } -------------------------------------------------------------------------------- /src/main/java/com/xcbeyond/common/file/chunk/service/FileService.java: -------------------------------------------------------------------------------- 1 | package com.xcbeyond.common.file.chunk.service; 2 | 3 | import org.springframework.web.multipart.MultipartFile; 4 | 5 | import javax.servlet.http.HttpServletRequest; 6 | import javax.servlet.http.HttpServletResponse; 7 | 8 | /** 9 | * @Auther: xcbeyond 10 | * @Date: 2019/5/9 23:01 11 | */ 12 | public interface FileService { 13 | void fileChunkUpload(long chunkId, MultipartFile multipartFile); 14 | /** 15 | * 文件分片下载 16 | * @param range 17 | * @param request 18 | * @param response 19 | */ 20 | void fileChunkDownload(String range, HttpServletRequest request, HttpServletResponse response); 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/xcbeyond/common/file/chunk/service/impl/FileServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.xcbeyond.common.file.chunk.service.impl; 2 | 3 | import com.xcbeyond.common.file.chunk.service.FileService; 4 | import org.springframework.stereotype.Service; 5 | import org.springframework.web.multipart.MultipartFile; 6 | 7 | import javax.servlet.http.HttpServletRequest; 8 | import javax.servlet.http.HttpServletResponse; 9 | import java.io.*; 10 | 11 | /** 12 | * 文件分片操作Service 13 | * @Auther: xcbeyond 14 | * @Date: 2019/5/9 23:02 15 | */ 16 | @Service 17 | public class FileServiceImpl implements FileService { 18 | //分片大小 19 | private static final long chunkSize = 1024; 20 | private static final String storePath = ""; 21 | 22 | public void fileChunkUpload(long chunkId, MultipartFile multipartFile) { 23 | String path = storePath; 24 | String fileName = multipartFile.getOriginalFilename(); 25 | 26 | String chunkFileName = fileName + ".chunk"; 27 | File chunkFile = new File(path, chunkFileName); 28 | if (!chunkFile.exists()) { 29 | chunkFile.getParentFile().mkdirs(); 30 | } 31 | RandomAccessFile raf = null; 32 | try { 33 | raf = new RandomAccessFile(chunkFile, "rw"); 34 | //计算写入文件的偏移位置 35 | long offset = (chunkId - 1) * chunkSize; 36 | raf.seek(offset); 37 | raf.write(multipartFile.getBytes()); 38 | 39 | } catch (FileNotFoundException e) { 40 | e.printStackTrace(); 41 | } catch (IOException e) { 42 | e.printStackTrace(); 43 | } finally { 44 | try { 45 | raf.close(); 46 | } catch (IOException e) { 47 | e.printStackTrace(); 48 | } 49 | } 50 | } 51 | 52 | /** 53 | * 文件分片下载 54 | * @param range http请求头Range,用于表示请求指定部分的内容。 55 | * 格式为:Range: bytes=start-end [start,end]表示,即是包含请求头的start及end字节的内容 56 | * @param request 57 | * @param response 58 | */ 59 | public void fileChunkDownload(String range, HttpServletRequest request, HttpServletResponse response) { 60 | //要下载的文件,此处以项目pom.xml文件举例说明。实际项目请根据实际业务场景获取 61 | File file = new File(System.getProperty("user.dir") + "\\pom.xml"); 62 | 63 | //开始下载位置 64 | long startByte = 0; 65 | //结束下载位置 66 | long endByte = file.length() - 1; 67 | 68 | //有range的话 69 | if (range != null && range.contains("bytes=") && range.contains("-")) { 70 | range = range.substring(range.lastIndexOf("=") + 1).trim(); 71 | String ranges[] = range.split("-"); 72 | try { 73 | //根据range解析下载分片的位置区间 74 | if (ranges.length == 1) { 75 | //情况1,如:bytes=-1024 从开始字节到第1024个字节的数据 76 | if (range.startsWith("-")) { 77 | endByte = Long.parseLong(ranges[0]); 78 | } 79 | //情况2,如:bytes=1024- 第1024个字节到最后字节的数据 80 | else if (range.endsWith("-")) { 81 | startByte = Long.parseLong(ranges[0]); 82 | } 83 | } 84 | //情况3,如:bytes=1024-2048 第1024个字节到2048个字节的数据 85 | else if (ranges.length == 2) { 86 | startByte = Long.parseLong(ranges[0]); 87 | endByte = Long.parseLong(ranges[1]); 88 | } 89 | 90 | } catch (NumberFormatException e) { 91 | startByte = 0; 92 | endByte = file.length() - 1; 93 | } 94 | } 95 | 96 | //要下载的长度 97 | long contentLength = endByte - startByte + 1; 98 | //文件名 99 | String fileName = file.getName(); 100 | //文件类型 101 | String contentType = request.getServletContext().getMimeType(fileName); 102 | 103 | //响应头设置 104 | //https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Accept-Ranges 105 | response.setHeader("Accept-Ranges", "bytes"); 106 | //Content-Type 表示资源类型,如:文件类型 107 | response.setHeader("Content-Type", contentType); 108 | //Content-Disposition 表示响应内容以何种形式展示,是以内联的形式(即网页或者页面的一部分),还是以附件的形式下载并保存到本地。 109 | // 这里文件名换成下载后你想要的文件名,inline表示内联的形式,即:浏览器直接下载 110 | response.setHeader("Content-Disposition", "inline;filename=pom.xml"); 111 | //Content-Length 表示资源内容长度,即:文件大小 112 | response.setHeader("Content-Length", String.valueOf(contentLength)); 113 | //Content-Range 表示响应了多少数据,格式为:[要下载的开始位置]-[结束位置]/[文件总大小] 114 | response.setHeader("Content-Range", "bytes " + startByte + "-" + endByte + "/" + file.length()); 115 | 116 | response.setStatus(response.SC_OK); 117 | response.setContentType(contentType); 118 | 119 | BufferedOutputStream outputStream = null; 120 | RandomAccessFile randomAccessFile = null; 121 | //已传送数据大小 122 | long transmitted = 0; 123 | try { 124 | randomAccessFile = new RandomAccessFile(file, "r"); 125 | outputStream = new BufferedOutputStream(response.getOutputStream()); 126 | byte[] buff = new byte[2048]; 127 | int len = 0; 128 | randomAccessFile.seek(startByte); 129 | //判断是否到了最后不足2048(buff的length)个byte 130 | while ((transmitted + len) <= contentLength && (len = randomAccessFile.read(buff)) != -1) { 131 | outputStream.write(buff, 0, len); 132 | transmitted += len; 133 | } 134 | //处理不足buff.length部分 135 | if (transmitted < contentLength) { 136 | len = randomAccessFile.read(buff, 0, (int) (contentLength - transmitted)); 137 | outputStream.write(buff, 0, len); 138 | transmitted += len; 139 | } 140 | 141 | outputStream.flush(); 142 | response.flushBuffer(); 143 | randomAccessFile.close(); 144 | } catch (IOException e) { 145 | e.printStackTrace(); 146 | } finally { 147 | try { 148 | if (randomAccessFile != null) { 149 | randomAccessFile.close(); 150 | } 151 | } catch (IOException e) { 152 | e.printStackTrace(); 153 | } 154 | } 155 | } 156 | } -------------------------------------------------------------------------------- /src/main/java/com/xcbeyond/common/security/JwtToken.java: -------------------------------------------------------------------------------- 1 | package com.xcbeyond.common.security; 2 | 3 | import com.alibaba.fastjson.JSONObject; 4 | import io.jsonwebtoken.*; 5 | import io.jsonwebtoken.impl.DefaultClock; 6 | 7 | import java.util.Date; 8 | import java.util.Map; 9 | import java.util.function.Function; 10 | /** 11 | * JWT(JSON Web Token) 工具类 12 | * @Auther: xcbeyond 13 | * @Date: 2019/5/24 13:45 14 | */ 15 | public class JwtToken { 16 | private static Clock clock = DefaultClock.INSTANCE; 17 | 18 | //密钥 19 | private String secret; 20 | 21 | //失效时间,秒 22 | private int expiration = 3600; 23 | 24 | public JwtToken(String secret) { 25 | this.secret = secret; 26 | } 27 | 28 | public JwtToken(String secret, int expiration) { 29 | this.secret = secret; 30 | this.expiration = expiration; 31 | } 32 | 33 | /** 34 | * 生成token 35 | * @param payload 36 | * @return 37 | */ 38 | public String generateToken(String payload) { 39 | JSONObject json = JSONObject.parseObject(payload); 40 | Map claims = json.getInnerMap(); 41 | return doGenerateToken(claims); 42 | } 43 | 44 | /** 45 | * 校验token 46 | * @param token token字符串 47 | * @param payload 48 | * @return 49 | */ 50 | public Boolean validateToken(String token, String payload) { 51 | boolean result; 52 | try { 53 | JSONObject jsonObject = JSONObject.parseObject(payload); 54 | String username = jsonObject.getString("username"); 55 | 56 | String tokenUsername = getUsernameFromToken(token); 57 | // Date created = getIssuedAtDateFromToken(token); 58 | result = username.equals(tokenUsername) 59 | && !isTokenExpired(token); 60 | // && !isCreatedBeforeLastPasswordReset(created, user.getLastPasswordResetDate()) 61 | 62 | } catch (ExpiredJwtException e) { 63 | e.printStackTrace(); 64 | result = false; 65 | } 66 | 67 | return result; 68 | } 69 | 70 | /** 71 | * 是否过期 72 | * @param token 73 | * @return 74 | */ 75 | private Boolean isTokenExpired(String token) { 76 | Date expiration = getExpirationDateFromToken(token); 77 | return expiration.before(clock.now()); 78 | } 79 | 80 | private String getUsernameFromToken(String token) { 81 | return getClaimFromToken(token, Claims::getSubject); 82 | } 83 | 84 | private Date getIssuedAtDateFromToken(String token) { 85 | return getClaimFromToken(token, Claims::getIssuedAt); 86 | } 87 | 88 | private Date getExpirationDateFromToken(String token) { 89 | return getClaimFromToken(token, Claims::getExpiration); 90 | } 91 | 92 | private T getClaimFromToken(String token, Function claimsResolver) { 93 | final Claims claims = getAllClaimsFromToken(token); 94 | return claimsResolver.apply(claims); 95 | } 96 | 97 | private Claims getAllClaimsFromToken(String token) { 98 | return Jwts.parser() 99 | .setSigningKey(secret) 100 | .parseClaimsJws(token) 101 | .getBody(); 102 | } 103 | 104 | private Boolean isCreatedBeforeLastPasswordReset(Date created, Date lastPasswordReset) { 105 | return (lastPasswordReset != null && created.before(lastPasswordReset)); 106 | } 107 | 108 | private Boolean ignoreTokenExpiration(String token) { 109 | // here you specify tokens, for that the expiration is ignored 110 | return false; 111 | } 112 | 113 | private String doGenerateToken(Map claims) { 114 | final Date createdDate = clock.now(); 115 | final Date expirationDate = calculateExpirationDate(createdDate); 116 | 117 | return Jwts.builder() 118 | .setClaims(claims) 119 | .setIssuedAt(createdDate) 120 | .setExpiration(expirationDate) 121 | .signWith(SignatureAlgorithm.HS512, secret) 122 | .compact(); 123 | } 124 | 125 | private Boolean canTokenBeRefreshed(String token, Date lastPasswordReset) { 126 | final Date created = getIssuedAtDateFromToken(token); 127 | return !isCreatedBeforeLastPasswordReset(created, lastPasswordReset) 128 | && (!isTokenExpired(token) || ignoreTokenExpiration(token)); 129 | } 130 | 131 | private String refreshToken(String token) { 132 | final Date createdDate = clock.now(); 133 | final Date expirationDate = calculateExpirationDate(createdDate); 134 | 135 | final Claims claims = getAllClaimsFromToken(token); 136 | claims.setIssuedAt(createdDate); 137 | claims.setExpiration(expirationDate); 138 | 139 | return Jwts.builder() 140 | .setClaims(claims) 141 | .signWith(SignatureAlgorithm.HS512, secret) 142 | .compact(); 143 | } 144 | 145 | private Date calculateExpirationDate(Date createdDate) { 146 | return new Date(createdDate.getTime() + expiration * 1000); 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /src/test/java/com/xcbeyond/common/DateUtilsTest.java: -------------------------------------------------------------------------------- 1 | package com.xcbeyond.common; 2 | 3 | import org.junit.Test; 4 | 5 | /** 6 | * com.xcbeyond.common.DateUtils测试类 7 | * @Auther: xcbeyond 8 | * @Date: 2019/4/30 15:33 9 | */ 10 | public class DateUtilsTest { 11 | 12 | @Test 13 | public void getCurrentDate() { 14 | System.out.println(DateUtils.getCurrentDate()); 15 | 16 | //指定时间格式,获取当前时间 17 | System.out.println(DateUtils.getCurrentDate("yyyy-MM-dd HH")); 18 | } 19 | 20 | /** 21 | * 取得某年某月有多少天 22 | */ 23 | @Test 24 | public void getDaysOfMonth() { 25 | String ym = "201904"; 26 | System.out.println(DateUtils.getDaysOfMonth(ym)); 27 | } 28 | } -------------------------------------------------------------------------------- /src/test/java/com/xcbeyond/common/StartApplication.java: -------------------------------------------------------------------------------- 1 | package com.xcbeyond.common; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import org.springframework.boot.SpringApplication; 6 | import org.springframework.boot.autoconfigure.SpringBootApplication; 7 | 8 | /** 9 | * springboot启动类。 10 | * 在该项目中目的用来启动一个tomcat,作为web容器 11 | * @Auther: xcbeyond 12 | * @Date: 2019/5/9 22:34 13 | */ 14 | @SpringBootApplication 15 | public class StartApplication { 16 | private static Logger logger = LoggerFactory.getLogger(StartApplication.class); 17 | 18 | public static void main(String[] args) { 19 | if(logger.isDebugEnabled()) { 20 | logger.debug("StartApplication starting..."); 21 | } 22 | SpringApplication.run(StartApplication.class, args); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/test/java/com/xcbeyond/common/communication/socket/SocketClientTest.java: -------------------------------------------------------------------------------- 1 | package com.xcbeyond.common.communication.socket; 2 | 3 | /** 4 | * SocketClient测试类 5 | * @Auther: xuchao 6 | * @Date: 2019/4/22 16:58 7 | */ 8 | public class SocketClientTest { 9 | public static void main(String[] args) { 10 | SocketClient socketClient = new SocketClient("127.0.0.1", 9999); 11 | String msg = "0000047010000 Q01710 743 OAApproveResult1468982058432dqga4ag2sbgpixru5qy08yp91q83tw73obkz279905598125900800xctestxc2019-04-20001同意"; 12 | String response = socketClient.sendMessage(msg); 13 | System.out.println("返回数据为:\n" + response); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/test/java/com/xcbeyond/common/communication/socket/SocketServerTest.java: -------------------------------------------------------------------------------- 1 | package com.xcbeyond.common.communication.socket; 2 | 3 | /** 4 | * 启动Socket Server 5 | * @Auther: xcbeyond 6 | * @Date: 2019/4/22 16:39 7 | */ 8 | public class SocketServerTest { 9 | public static void main(String[] args) { 10 | new SocketServer(9999).start(); 11 | } 12 | } -------------------------------------------------------------------------------- /src/test/java/com/xcbeyond/common/security/JwtTokenTest.java: -------------------------------------------------------------------------------- 1 | package com.xcbeyond.common.security; 2 | 3 | import com.alibaba.fastjson.JSONObject; 4 | import org.junit.Assert; 5 | import org.junit.Before; 6 | import org.junit.Test; 7 | 8 | import java.util.UUID; 9 | 10 | /** 11 | * JwtToken测试用例 12 | * @Auther: xcbeyond 13 | * @Date: 2019/5/23 15:03 14 | */ 15 | public class JwtTokenTest { 16 | private JwtToken jwtToken; 17 | private String payload; 18 | 19 | @Before 20 | public void init() { 21 | //UUID随机值作为密钥 22 | jwtToken = new JwtToken(UUID.randomUUID().toString()); 23 | JSONObject json = new JSONObject(); 24 | json.put("username", "xcbeyond"); 25 | payload = json.toJSONString(); 26 | } 27 | 28 | /** 29 | *生成token 30 | */ 31 | @Test 32 | public void createToken() { 33 | String token = jwtToken.generateToken(payload); 34 | System.out.println(token); 35 | } 36 | 37 | /** 38 | * 校验token 39 | */ 40 | @Test 41 | public void validateToken() { 42 | String token = "eyJhbGciOiJIUzUxMiJ9.eyJleHAiOjE1NTg2MDE1OTUsImlhdCI6MTU1ODYwMTUzNSwidXNlcm5hbWUiOiJ4dWNoYW8ifQ.H7GA1Gi3uPdAyDGxoW-KAOBDVIpKkBiyBoO3c0_PHDuGtlY48Ogd09I0yWSvNs0s3TRWdQvAJ3Uy5UBYQXMQig"; 43 | boolean result = jwtToken.validateToken(token, payload); 44 | Assert.assertTrue(result); 45 | } 46 | } -------------------------------------------------------------------------------- /src/test/java/resources/application.properties: -------------------------------------------------------------------------------- 1 | server.port=8080 --------------------------------------------------------------------------------