package org.springblade.common.utils; import cn.hutool.core.io.FileUtil; import cn.hutool.core.lang.func.Func; import cn.hutool.http.HttpUtil; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.aliyuncs.utils.IOUtils; import com.drew.imaging.ImageMetadataReader; import com.drew.imaging.ImageProcessingException; import com.drew.metadata.Metadata; import com.drew.metadata.exif.ExifIFD0Directory; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import org.apache.commons.lang.StringUtils; import org.springframework.util.CollectionUtils; import javax.imageio.IIOImage; import javax.imageio.ImageIO; import javax.imageio.ImageWriteParam; import javax.imageio.ImageWriter; import java.awt.*; import java.awt.color.ColorSpace; import java.awt.color.ICC_ColorSpace; import java.awt.geom.AffineTransform; import java.awt.image.AffineTransformOp; import java.awt.image.BufferedImage; import java.awt.image.ColorConvertOp; import java.io.*; import java.math.BigDecimal; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLConnection; import java.time.LocalDate; import java.util.*; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.stream.Stream; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; import com.drew.metadata.MetadataException; import org.springframework.util.ResourceUtils; /** * 通用工具类 * * @author Chill */ public class CommonUtil { private static final double INCH_TO_CM = 2.54; public static Boolean checkBigDecimal(Object value) { try { if (value != null && StringUtils.isNotEmpty(value.toString())) { new BigDecimal(value.toString()); return true; } } catch (Exception e) { e.printStackTrace(); } return false; } public static void removeFile(List removeList) { for (String fileUrl : removeList) { try { FileUtil.del(new File(fileUrl)); } catch (Exception e) { e.printStackTrace(); } } } public static String handleNull(Object obj) { if (null == obj) { return ""; } else { return obj.toString().trim(); } } public static String join(Object... args) { if (args != null) { if (args.length > 2) { List list = Arrays.stream(args).limit(args.length - 1).map(CommonUtil::handleNull).collect(Collectors.toList()); String split = handleNull(args[args.length - 1]); return join(list, split); } else { return handleNull(args[0]); } } else { return ""; } } public static String join(List list, String split) { StringBuilder sb = new StringBuilder(); if (list != null && list.size() > 0) { for (String str : list) { if (StringUtils.isNotEmpty(str)) { sb.append(str).append(split); } } if (sb.length() > 0 && StringUtils.isNotEmpty(split)) { sb.delete(sb.length() - split.length(), sb.length()); } } return sb.toString(); } public static Matcher matcher(String regex, String value) { Pattern pattern = Pattern.compile(regex); return pattern.matcher(value); } /** * 文件流转化为File */ public static void convert(InputStream inputStream, String targetFile) throws IOException { // 创建文件输出流 FileOutputStream outputStream = new FileOutputStream(targetFile); // 创建缓冲区 byte[] buffer = new byte[1024]; int bytesRead; // 从输入流读取数据并写入输出流 while ((bytesRead = inputStream.read(buffer)) != -1) { outputStream.write(buffer, 0, bytesRead); } // 关闭流 outputStream.close(); inputStream.close(); } /** * 根据OSS文件路径获取文件输入流 */ public static InputStream getOSSInputStream(String urlStr) { try { urlStr = replaceOssUrl(urlStr); //获取OSS文件流 URL url = new URL(urlStr); URLConnection conn = url.openConnection(); /* // 设置连接超时时间 conn.setConnectTimeout(10000); // 5秒 // 设置读取超时时间 conn.setReadTimeout(10000); // 5秒*/ conn.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)"); return conn.getInputStream(); } catch (Exception e) { System.out.println("zw-----------"); return null; } } /** * 根据OSS文件路径获取文件输入流 */ public static InputStream getOSSInputStreamTow(String urlStr) throws Exception { //获取OSS文件流 urlStr = replaceOssUrl(urlStr); URL imageUrl = new URL(urlStr); HttpURLConnection conn = null; try { conn = (HttpURLConnection) imageUrl.openConnection(); conn.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)"); return conn.getInputStream(); } catch (IOException e) { e.printStackTrace(); if (conn != null) { conn.disconnect(); //关闭网络连接 } throw new Exception("获取图片输入流失败!URL:" + urlStr, e); } } /** * 获取字节数组 */ public static synchronized byte[] InputStreamToBytes(InputStream is) { BufferedInputStream bis = new BufferedInputStream(is); ByteArrayOutputStream os = new ByteArrayOutputStream(); int date = -1; while (true) { try { if (!((date = bis.read()) != -1)) break; os.write(date); } catch (IOException e) { throw new RuntimeException(e); } } return os.toByteArray(); } /** * 随机生成短信验证码 * * @param length 生成长度 */ public static String getCharAndNumber(int length) { StringBuilder val = new StringBuilder(); Random random = new Random(); for (int i = 0; i < length; i++) { String charOrNum = random.nextInt(2) % 2 == 0 ? "char" : "num"; if ("char".equalsIgnoreCase(charOrNum)) { int choice = random.nextInt(2) % 2 == 0 ? 65 : 97; val.append((char) (choice + random.nextInt(26))); } else { val.append(random.nextInt(10)); } } return val.toString(); } /** * 判断参数是否是数字 * * @param value 需要判断数据 * @return 判断结果,数字则为true,反之false */ public static boolean checkIsBigDecimal(Object value) { try { if (value != null && StringUtils.isNotEmpty(String.valueOf(value))) { new BigDecimal(String.valueOf(value)); return true; } else { return false; } } catch (Exception e) { return false; } } /** * 根据每页信息分组 */ public static List> getBatchSize(List allIds, int size) { List> batchIds = new ArrayList<>(); if (allIds == null || allIds.size() == 0 || size <= 0) { return batchIds; } int i = 0; List tmp = new ArrayList<>(); for (T map : allIds) { tmp.add(map); i++; if (i % size == 0 || i == allIds.size()) { batchIds.add(tmp); tmp = new ArrayList<>(); } } return batchIds; } /** * @param src * @throws IOException * @throws ClassNotFoundException */ public static List deepCopy(List src) throws IOException, ClassNotFoundException { ByteArrayOutputStream byteOut = new ByteArrayOutputStream(); ObjectOutputStream out = new ObjectOutputStream(byteOut); out.writeObject(src); ByteArrayInputStream byteIn = new ByteArrayInputStream(byteOut.toByteArray()); ObjectInputStream in = new ObjectInputStream(byteIn); @SuppressWarnings("unchecked") List dest = (List) in.readObject(); return dest; } /** * Description: Java8 Stream分割list集合 * * @param list 集合数据 * @param splitSize 几个分割一组 * @return 集合分割后的集合 */ public static List> splitList(List list, int splitSize) { //判断集合是否为空 if (CollectionUtils.isEmpty(list)) return Collections.emptyList(); //计算分割后的大小 int maxSize = (list.size() + splitSize - 1) / splitSize; //开始分割 return Stream.iterate(0, n -> n + 1) .limit(maxSize) .parallel() .map(a -> list.parallelStream().skip(a * splitSize).limit(splitSize).collect(Collectors.toList())) .filter(b -> !b.isEmpty()) .collect(Collectors.toList()); } /** * Map拆分 (指定分组大小) * * @param map Map * @param chunkSize 每个分组的大小 (>=1) * @param Key * @param Value * @return 子Map列表 */ public static List> splitByChunkSize(Map map, int chunkSize) { if (Objects.isNull(map) || map.isEmpty() || chunkSize < 1) { //空map或者分组大小<1,无法拆分 return Collections.emptyList(); } int mapSize = map.size(); //键值对总数 int groupSize = mapSize / chunkSize + (mapSize % chunkSize == 0 ? 0 : 1); //计算分组个数 List> list = Lists.newArrayListWithCapacity(groupSize); //子Map列表 if (chunkSize >= mapSize) { //只能分1组的情况 list.add(map); return list; } int count = 0; //每个分组的组内计数 Map subMap = Maps.newHashMapWithExpectedSize(chunkSize); //子Map for (Map.Entry entry : map.entrySet()) { if (count < chunkSize) { //给每个分组放chunkSize个键值对,最后一个分组可能会装不满 subMap.put(entry.getKey(), entry.getValue()); count++; //组内计数+1 } else { //结束上一个分组 list.add(subMap); //当前分组装满了->加入列表 //开始下一个分组 subMap = Maps.newHashMapWithExpectedSize(chunkSize); //新的分组 subMap.put(entry.getKey(), entry.getValue()); //添加当前键值对 count = 1; //组内计数重置为1 } } list.add(subMap); //添加最后一个分组 return list; } /** * Map拆分(指定分组个数) * * @param map Map * @param groupSize 分组个数 (>=1) * @param Key * @param Value * @return 子Map列表 */ public static List> splitByGroupSize(Map map, int groupSize) { if (Objects.isNull(map) || map.isEmpty() || groupSize < 1) { //空map或者分组数<1,无法拆分 return Collections.emptyList(); } List> list = Lists.newArrayListWithCapacity(groupSize); if (groupSize == 1) { //只有1个分组的情况 list.add(map); return list; } int mapSize = map.size(); //键值对总数 int chunkIndex = 0; //当前分组的下标,[0, groupSize-1] int restCount = mapSize % groupSize; //平均后剩余的键值对数 int chunkSize0 = mapSize / groupSize; //每个分组键值对数量 int chunkSize1 = chunkSize0 + 1; //多分一个 int chunkSize = chunkIndex < restCount ? chunkSize1 : chunkSize0; //实际每组的大小(前面的部分分组可能会多分1个) int count = 0; //每个分组的组内计数 Map subMap = Maps.newHashMapWithExpectedSize(chunkSize);//子Map for (Map.Entry entry : map.entrySet()) { if (count < chunkSize) { //每个分组按实际分组大小(chunkSize)加入键值对 subMap.put(entry.getKey(), entry.getValue()); count++; //组内计数+1 } else { //结束上一个分组 list.add(subMap); //当前分组装满了->加入列表 chunkIndex++; //分组个数+1 //开始下一个分组 chunkSize = chunkIndex < restCount ? chunkSize1 : chunkSize0; //重新计算分组大小 subMap = Maps.newHashMapWithExpectedSize(chunkSize); //新的分组 subMap.put(entry.getKey(), entry.getValue()); //添加当前键值对 count = 1; //组内计数重置为1 } } list.add(subMap); //添加最后一个分组 return list; } /** * 流写入文件 * * @param inputStream 文件输入流 * @param file 输出文件 */ public static void inputStreamToFile(InputStream inputStream, File file) { try { OutputStream os = new FileOutputStream(file); int bytesRead = 0; byte[] buffer = new byte[8192]; while ((bytesRead = inputStream.read(buffer, 0, 8192)) != -1) { os.write(buffer, 0, bytesRead); } os.close(); inputStream.close(); } catch (Exception e) { e.printStackTrace(); } } /** * 删除文件夹下所有文件 * * @param path * @return */ public static boolean deleteDir(String path) { File file = new File(path); if (!file.exists()) {//判断是否待删除目录是否存在 System.err.println("The dir are not exists!"); return false; } String[] content = file.list();//取得当前目录下所有文件和文件夹 for (String name : content) { File temp = new File(path, name); if (temp.isDirectory()) {//判断是否是目录 deleteDir(temp.getAbsolutePath());//递归调用,删除目录里的内容 temp.delete();//删除空目录 } else { if (!temp.delete()) {//直接删除文件 System.err.println("Failed to delete " + name); } } } return true; } /** * 压缩指定路径下的文件夹 * * @param filesPath * @throws Exception */ public static void packageZip(String filesPath, String zipUrl) throws Exception { // 要被压缩的文件夹 File file = new File(filesPath); //需要压缩的文件夹 File folder = new File(zipUrl); if (!folder.exists() && !folder.isDirectory()) { folder.mkdirs(); } File zipFile = new File(zipUrl + "/" + "localArchive.zip"); //放于和需要压缩的文件夹同级目录 ZipOutputStream zipOut = new ZipOutputStream(new FileOutputStream(zipFile)); isDirectory(file, zipOut, "", true); //判断是否为文件夹 zipOut.close(); } public static void isDirectory(File file, ZipOutputStream zipOutputStream, String filePath, boolean flag) throws IOException { //判断是否为问加减 if (file.isDirectory()) { File[] files = file.listFiles(); //获取该文件夹下所有文件(包含文件夹) filePath = flag == true ? file.getName() : filePath + File.separator + file.getName(); //首次为选中的文件夹,即根目录,之后递归实现拼接目录 for (int i = 0; i < files.length; ++i) { //判断子文件是否为文件夹 if (files[i].isDirectory()) { //进入递归,flag置false 即当前文件夹下仍包含文件夹 isDirectory(files[i], zipOutputStream, filePath, false); } else { //不为文件夹则进行压缩 InputStream input = new FileInputStream(files[i]); zipOutputStream.putNextEntry(new ZipEntry(filePath + File.separator + files[i].getName())); int temp = 0; while ((temp = input.read()) != -1) { zipOutputStream.write(temp); } input.close(); } } } else { //将子文件夹下的文件进行压缩 InputStream input = new FileInputStream(file); zipOutputStream.putNextEntry(new ZipEntry(file.getPath())); int temp = 0; while ((temp = input.read()) != -1) { zipOutputStream.write(temp); } input.close(); } } /** * @param urlStr * @return 返回Url资源大小 * @throws IOException */ public static long getResourceLength(String urlStr) throws IOException { urlStr = replaceOssUrl(urlStr); URL url = new URL(urlStr); URLConnection urlConnection = url.openConnection(); urlConnection.connect(); //返回响应报文头字段Content-Length的值 return urlConnection.getContentLength(); } /** * 图片缩放、压缩、旋转处理 * * @return * @throws IOException * @throws ImageProcessingException * @throws MetadataException */ public static void main(String[] args) throws IOException { // String imgurl = "/Users/hongchuangyanfa/Desktop/excel/432123.jpg"; // String imgurl = "/Users/hongchuangyanfa/Desktop/excel/123.jpg"; String imgurl = "/Users/hongchuangyanfa/Downloads/bccb09f5e2caa85611d380f596d4febb.jpg"; File file = ResourceUtils.getFile(imgurl); byte[] imageData = InputStreamToBytes(new FileInputStream(file)); ByteArrayInputStream bais = new ByteArrayInputStream(imageData); try { ImageIO.setUseCache(false); BufferedImage originalImage = ImageIO.read(bais); } catch (IOException e) { throw new RuntimeException(e); } System.out.println("图片转换并保存成功!"); } public static byte[] compressImage(byte[] imageData) throws IOException, ImageProcessingException, MetadataException { // 读取原始图像(处理旋转问题) Metadata metadata = ImageMetadataReader.readMetadata(new ByteArrayInputStream(imageData)); if (metadata.containsDirectoryOfType(ExifIFD0Directory.class)) { ExifIFD0Directory exifIFD0Directory = metadata.getFirstDirectoryOfType(ExifIFD0Directory.class); if (exifIFD0Directory.containsTag(ExifIFD0Directory.TAG_ORIENTATION)) { // 获取 Orientation 标签的值 int orientation = exifIFD0Directory.getInt(ExifIFD0Directory.TAG_ORIENTATION); // 需要旋转图片 // 1 无需纠正 2 水平翻转(镜像)3 垂直翻转(旋转180°) 4 水平翻转+垂直翻转 5 水平翻转+旋转90° // 6 旋转90° 7 水平翻转+旋转270° 8 +旋转270° if (orientation > 1) { BufferedImage originalImage = ImageIO.read(new ByteArrayInputStream(imageData)); AffineTransform transform = new AffineTransform(); if (orientation == 3) { transform.rotate(Math.PI, originalImage.getWidth() / 2, originalImage.getHeight() / 2); } else if (orientation == 6) { transform.rotate(Math.PI / 2, originalImage.getWidth() / 2, originalImage.getHeight() / 2); } else if (orientation == 8) { transform.rotate(-Math.PI / 2, originalImage.getWidth() / 2, originalImage.getHeight() / 2); } AffineTransformOp op = new AffineTransformOp(transform, AffineTransformOp.TYPE_BILINEAR); originalImage = op.filter(originalImage, null); ByteArrayOutputStream baos = new ByteArrayOutputStream(); ImageIO.write(originalImage, "jpg", baos); imageData = baos.toByteArray(); } } } // 缩放图像 String formatName = "JPEG"; ByteArrayInputStream bais = new ByteArrayInputStream(imageData); BufferedImage originalImage = ImageIO.read(bais); long sizeLimit = 1024*1024*5; //5M int width = 768; int height = 1024; Image scaledImage = originalImage.getScaledInstance(width, height, Image.SCALE_SMOOTH); BufferedImage resizedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); resizedImage.getGraphics().drawImage(scaledImage, 0, 0, null); // 压缩图像 ByteArrayOutputStream baos = new ByteArrayOutputStream(); ImageIO.write(resizedImage, formatName, baos); if (baos.size() <= sizeLimit) { // 图片大小已经小于等于目标大小,直接返回原始数据 return baos.toByteArray(); } float quality = 0.9f; // 初始化压缩质量 int retries = 10; // 最多尝试 10 次 while (baos.size() > sizeLimit && retries > 0) { // 压缩图像并重新计算压缩质量 byte[] data = baos.toByteArray(); bais = new ByteArrayInputStream(data); BufferedImage compressedImage = ImageIO.read(bais); baos.reset(); ImageWriter writer = null; Iterator writers = ImageIO.getImageWritersByFormatName(formatName); if (writers.hasNext()) { writer = writers.next(); } else { throw new IllegalArgumentException("Unsupported image format: " + formatName); } ImageWriteParam writeParam = writer.getDefaultWriteParam(); writeParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); writeParam.setCompressionQuality(quality); writer.setOutput(ImageIO.createImageOutputStream(baos)); writer.write(null, new IIOImage(compressedImage, null, null), writeParam); writer.dispose(); float ratio = sizeLimit * 1.0f / baos.size(); quality *= Math.sqrt(ratio); retries--; } return baos.toByteArray(); } /** * 图片压缩 * * @param imageData * @return * @throws IOException * @throws ImageProcessingException * @throws MetadataException */ public static byte[] compressImage2(byte[] imageData) throws IOException { // 缩放图像 String formatName = "JPEG"; ByteArrayInputStream bais = new ByteArrayInputStream(imageData); BufferedImage originalImage = ImageIO.read(bais); long sizeLimit = 512000; //358KB int width = originalImage.getWidth(); int height = originalImage.getHeight(); Image scaledImage = originalImage.getScaledInstance(width, height, Image.SCALE_SMOOTH); BufferedImage resizedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); resizedImage.getGraphics().drawImage(scaledImage, 0, 0, null); // 压缩图像 ByteArrayOutputStream baos = new ByteArrayOutputStream(); ImageIO.write(resizedImage, formatName, baos); if (baos.size() <= sizeLimit) { // 图片大小已经小于等于目标大小,直接返回原始数据 return baos.toByteArray(); } float quality = 0.7f; // 初始化压缩质量 int retries = 10; // 最多尝试 10 次 while (baos.size() > sizeLimit && retries > 0) { // 压缩图像并重新计算压缩质量 bais = new ByteArrayInputStream(imageData); originalImage = ImageIO.read(bais); float width2 = originalImage.getWidth() * quality; float height2 = originalImage.getHeight() * quality; scaledImage = originalImage.getScaledInstance(width, height, Image.SCALE_SMOOTH); resizedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); resizedImage.getGraphics().drawImage(scaledImage, 0, 0, null); // 压缩图像 baos = new ByteArrayOutputStream(); ImageIO.write(resizedImage, formatName, baos); if (baos.size() <= sizeLimit) { // 图片大小已经小于等于目标大小,直接返回原始数据 return baos.toByteArray(); } byte[] data = baos.toByteArray(); bais = new ByteArrayInputStream(data); BufferedImage compressedImage = ImageIO.read(bais); baos.reset(); ImageWriter writer = null; Iterator writers = ImageIO.getImageWritersByFormatName(formatName); if (writers.hasNext()) { writer = writers.next(); } else { throw new IllegalArgumentException("Unsupported image format: " + formatName); } ImageWriteParam writeParam = writer.getDefaultWriteParam(); writeParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); writeParam.setCompressionQuality(quality); writer.setOutput(ImageIO.createImageOutputStream(baos)); writer.write(null, new IIOImage(compressedImage, null, null), writeParam); writer.dispose(); float ratio = sizeLimit * 1.0f / baos.size(); quality *= Math.sqrt(ratio); retries--; } return baos.toByteArray(); } /** * 根据起止日期获取工作日 * * @return */ public static int getWorkDays(LocalDate startTime, LocalDate endTime) { if (startTime.compareTo(endTime) > 0) { return -1; } // if (startTime.compareTo(endTime) == 0){ // return 1; // } StringBuilder str = new StringBuilder(); List list = new ArrayList<>(); while (!startTime.equals(endTime)) { str.append("d=" + startTime + "&"); list.add(startTime.toString()); startTime = startTime.plusDays(1L); } str.append("d=" + endTime + "&"); list.add(endTime.toString()); str.append("type=Y"); String post = HttpUtil.get("http://timor.tech/api/holiday/batch?" + str.toString()); JSONObject jsonObject = JSON.parseObject(post).getJSONObject("type"); System.out.println(jsonObject); int workDays = 0; for (String s : list) { Map map = JSONObject.parseObject(jsonObject.get(s).toString(), Map.class); int type = (int) map.get("type"); if (type == 0 || type == 3) { workDays++; } } return workDays; } public static String replaceOssUrl(String url) { //本地部署- 甬台温 if (url.indexOf("183.247.216.148") >= 0 || url.indexOf("152.168.2.15") >= 0) { // 如果当前环境变量不包含linuxtesttest,则替换URL中的oss路径 if (SystemUtils.isMacOs() || SystemUtils.isWindows()) { url = url.replace("https://", "http://").replace(":9000//", ":9000/"); } else { url = url.replace("https://", "http://").replace("183.247.216.148", "152.168.2.15").replace(":9000//", ":9000/"); } } return url; } /** * webp文件转字节数组 * * @param * @return */ public static byte[] webpToPngBytes(InputStream inputStream) { ByteArrayOutputStream bos = new ByteArrayOutputStream(); try { BufferedImage webpImage = ImageIO.read(inputStream); ImageIO.write(webpImage, "png", bos); } catch (Exception e) { e.printStackTrace(); } return bos.toByteArray(); } // 图片厘米转像素 public static int cmToPx(double cm) { double pixelsPerCm = 90 / INCH_TO_CM; return (int) Math.round(cm * pixelsPerCm); } // 图片厘米转像素 public static double pxToCm(int px) { double cmPerPixel = INCH_TO_CM / 90; //保留两位小数 return Math.round(px * cmPerPixel * 100) / 100.0; } }