小程序解决大问题-物流系统磁盘爆满问题处理

打印 上一主题 下一主题

主题 821|帖子 821|积分 2463

晚上七点,煤矿调运的物流调度系统突然磁盘报名导致服务瓦解。系统用的是微服务,没有详细操作说明,也不敢动,运煤车辆排起了长队,只能联系厂家处理。好在经过30多分钟的处理,服务终于启动,系统运行正常。经过排查是一台后端服务器存储了车辆图片,导致磁盘满了,数据库停止运行。以前也有雷同的问题,每次都会影响半个小时到一个小时。
作为程序员,分析了该服务器存储的图片都是抓取的原始图片,每张都在3-5M。十几万张图片如果压缩到几百k,磁盘容量问题就应该解决了。跟厂家沟通了下,图片只是最近七天的有用,而且大部分都在一天内已经传到第三方平台,可以压缩。
计划方案:

  • 遍历 d://ftp,下面的文件及文件夹,找出 全部jpeg图片;
  • 使用thumbnailator将图片压缩,并更换源文件;
  • 计算压缩前后的文件夹大小;
  • 计算压缩比例;
  • 记载处理日志;
  • 写入 windows 使命计划程序;
  • 使用多线程,考虑生产服务器,严格限制CPU和内存占用。
​压缩前:

​压缩后:

压缩后,图片车牌依然可以被识别,不影响使用:

程序:
  1. public class App {
  2.     private static final Logger logger = LoggerFactory.getLogger(App.class);
  3.     private static final int THREAD_POOL_SIZE = Math.max(1, Runtime.getRuntime().availableProcessors() / 2);
  4.     private static final AtomicInteger filesProcessed = new AtomicInteger(0);
  5.     private static int totalFilesToProcess = 0;
  6.     public static void main(String[] args) {
  7.         // 创建log文件夹
  8.         File logDir = new File("log");
  9.         if (!logDir.exists()) {
  10.             logDir.mkdir();
  11.         }
  12.         logger.info("线程池大小:{}", THREAD_POOL_SIZE);
  13.         Scanner scanner = new Scanner(System.in);
  14.         System.out.println("请输入文件夹路径(例如:d:\\ftp):");
  15.         String folderPath = scanner.nextLine();
  16.         File dir = new File(folderPath);
  17.         if (!dir.exists() || !dir.isDirectory()) {
  18.             logger.error("文件夹路径不存在: {}", folderPath);
  19.             return;
  20.         }
  21.         logger.info("开始处理文件夹: {}", folderPath);
  22.         totalFilesToProcess = countFiles(dir);
  23.         long originalSize = calculateDirectorySize(dir);
  24.         logger.info("原始文件夹大小: {}", formatBytes(originalSize));
  25.         logger.info("开始压缩图片");
  26.         long startTime = System.currentTimeMillis();
  27.         ExecutorService executorService = Executors.newFixedThreadPool(THREAD_POOL_SIZE);
  28.         traverseDirectory(dir, executorService);
  29.         executorService.shutdown();
  30.         try {
  31.             executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
  32.         } catch (InterruptedException e) {
  33.             Thread.currentThread().interrupt();
  34.             logger.error("线程池被中断: {}", e.getMessage());
  35.         }
  36.         long endTime = System.currentTimeMillis();
  37.         long compressedSize = calculateDirectorySize(dir);
  38.         logger.info("总耗时: {} 秒", (endTime - startTime) / 1000.0);
  39.         logger.info("压缩后文件夹总大小: {}", formatBytes(compressedSize));
  40.         double compressionRatio = (compressedSize == originalSize) ? 1.0 : (double) compressedSize / originalSize;
  41.         logger.info("压缩比例: {}%", String.format("%.2f", compressionRatio * 100));
  42.         logger.info("压缩图片完成");
  43.     }
  44.     public static void traverseDirectory(File dir, ExecutorService executorService) {
  45.         File[] files = dir.listFiles();
  46.         if (files != null) {
  47.             for (File file : files) {
  48.                 if (file.isDirectory()) {
  49.                     traverseDirectory(file, executorService);
  50.                 } else if (isJpegFile(file.getName())) {
  51.                     executorService.submit(() -> processImage(file));
  52.                 }
  53.             }
  54.         }
  55.     }
  56.     private static boolean isJpegFile(String fileName) {
  57.         String lowerCaseName = fileName.toLowerCase();
  58.         return lowerCaseName.endsWith(".jpeg") || lowerCaseName.endsWith(".jpg");
  59.     }
  60.     private static void processImage(File file) {
  61.         compressImage(file);
  62.         int processed = filesProcessed.incrementAndGet();
  63.         if (processed % 100 == 0 || processed == totalFilesToProcess) {
  64.             logger.info("已处理 {} / {} 文件", processed, totalFilesToProcess);
  65.         }
  66.     }
  67.     public static void compressImage(File file) {
  68.         try {
  69.             Thumbnails.Builder<File> builder = Thumbnails.of(file);
  70.             java.awt.image.BufferedImage originalImage = builder.scale(1).asBufferedImage();
  71.             int originalWidth = originalImage.getWidth();
  72.             if (originalWidth > 1280) {
  73.                 double scale = (double) 1280 / originalWidth;
  74.                 builder.scale(scale)
  75.                        .outputQuality(0.6)
  76.                        .toFile(file);
  77.                 logger.info("压缩图片: {}", file.getPath());
  78.             }
  79.         } catch (IOException e) {
  80.             logger.error("处理图片时出错: {} - {}", file.getPath(), e.getMessage());
  81.         }
  82.     }
  83.     /**
  84.      * 计算文件夹大小
  85.      * @param dir
  86.      * @return
  87.      */
  88.     public static long calculateDirectorySize(File dir) {
  89.         long totalSize = 0;
  90.         for (File file : dir.listFiles()) {
  91.             if (file.isDirectory()) {
  92.                 totalSize += calculateDirectorySize(file);
  93.             } else {
  94.                 totalSize += file.length();
  95.             }
  96.         }
  97.         return totalSize;
  98.     }
  99.     /**
  100.      * 转换文件大小为可读格式
  101.      * @param bytes
  102.      * @return
  103.      */
  104.     public static String formatBytes(long bytes) {
  105.         String[] units = {"B", "KB", "MB", "GB", "TB"};
  106.         int i = 0;
  107.         while (bytes >= 1024 && i < units.length - 1) {
  108.             bytes /= 1024;
  109.             i++;
  110.         }
  111.         return String.format("%.2f %s", bytes / 1.0, units[i]);
  112.     }
  113.     /**
  114.      * 计算文件夹下图片总数量
  115.      * @param dir
  116.      * @return
  117.      */
  118.     public static int countFiles(File dir) {
  119.         int count = 0;
  120.         for (File file : dir.listFiles()) {
  121.             if (file.isDirectory()) {
  122.                 count += countFiles(file);
  123.             } else if (file.getName().toLowerCase().endsWith(".jpeg") ||
  124.                     file.getName().toLowerCase().endsWith(".jpg")) {
  125.                 count++;
  126.             }
  127.         }
  128.         return count;
  129.     }
  130. }
复制代码
稳定运行两个月,磁盘空出200G。再也不用手忙脚乱地联系客服,重启服务了。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

九天猎人

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表