当前位置: 首页 > article >正文

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

晚上七点,煤矿调运的物流调度系统突然磁盘报名导致服务崩溃。系统用的是微服务,没有详细操作说明,也不敢动,运煤车辆排起了长队,只能联系厂家处理。好在经过30多分钟的处理,服务终于启动,系统运行正常。经过排查是一台后端服务器存储了车辆图片,导致磁盘满了,数据库停止运行。以前也有类似的问题,每次都会影响半个小时到一个小时。

作为程序员,分析了该服务器存储的图片都是抓取的原始图片,每张都在3-5M。十几万张图片如果压缩到几百k,磁盘容量问题就应该解决了。跟厂家沟通了下,图片只是最近七天的有用,而且大部分都在一天内已经传到第三方平台,可以压缩。

设计方案:

  1. 遍历 d://ftp,下面的文件及文件夹,找出 所有jpeg图片;
  2. 使用thumbnailator将图片压缩,并替换源文件;
  3. 计算压缩前后的文件夹大小;
  4. 计算压缩比例;
  5. 记录处理日志;
  6. 写入 windows 任务计划程序;
  7. 使用多线程,考虑生产服务器,严格限制CPU和内存占用。

​压缩前:
在这里插入图片描述

​压缩后:
在这里插入图片描述
压缩后,图片车牌依然可以被识别,不影响使用:
在这里插入图片描述

程序:

public class App {
    private static final Logger logger = LoggerFactory.getLogger(App.class);
    private static final int THREAD_POOL_SIZE = Math.max(1, Runtime.getRuntime().availableProcessors() / 2);
    private static final AtomicInteger filesProcessed = new AtomicInteger(0);
    private static int totalFilesToProcess = 0;

    public static void main(String[] args) {
        // 创建log文件夹
        File logDir = new File("log");
        if (!logDir.exists()) {
            logDir.mkdir();
        }

        logger.info("线程池大小:{}", THREAD_POOL_SIZE);
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入文件夹路径(例如:d:\\ftp):");
        String folderPath = scanner.nextLine();
        File dir = new File(folderPath);

        if (!dir.exists() || !dir.isDirectory()) {
            logger.error("文件夹路径不存在: {}", folderPath);
            return;
        }

        logger.info("开始处理文件夹: {}", folderPath);

        totalFilesToProcess = countFiles(dir);
        long originalSize = calculateDirectorySize(dir);
        logger.info("原始文件夹大小: {}", formatBytes(originalSize));
        logger.info("开始压缩图片");
        long startTime = System.currentTimeMillis();
        ExecutorService executorService = Executors.newFixedThreadPool(THREAD_POOL_SIZE);
        traverseDirectory(dir, executorService);
        executorService.shutdown();
        try {
            executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            logger.error("线程池被中断: {}", e.getMessage());
        }

        long endTime = System.currentTimeMillis();
        long compressedSize = calculateDirectorySize(dir);
        logger.info("总耗时: {} 秒", (endTime - startTime) / 1000.0);
        logger.info("压缩后文件夹总大小: {}", formatBytes(compressedSize));
        double compressionRatio = (compressedSize == originalSize) ? 1.0 : (double) compressedSize / originalSize;
        logger.info("压缩比例: {}%", String.format("%.2f", compressionRatio * 100));
        logger.info("压缩图片完成");
    }

    public static void traverseDirectory(File dir, ExecutorService executorService) {
        File[] files = dir.listFiles();
        if (files != null) {
            for (File file : files) {
                if (file.isDirectory()) {
                    traverseDirectory(file, executorService);
                } else if (isJpegFile(file.getName())) {
                    executorService.submit(() -> processImage(file));
                }
            }
        }
    }

    private static boolean isJpegFile(String fileName) {
        String lowerCaseName = fileName.toLowerCase();
        return lowerCaseName.endsWith(".jpeg") || lowerCaseName.endsWith(".jpg");
    }

    private static void processImage(File file) {
        compressImage(file);
        int processed = filesProcessed.incrementAndGet();
        if (processed % 100 == 0 || processed == totalFilesToProcess) {
            logger.info("已处理 {} / {} 文件", processed, totalFilesToProcess);
        }
    }

    public static void compressImage(File file) {
        try {
            Thumbnails.Builder<File> builder = Thumbnails.of(file);
            java.awt.image.BufferedImage originalImage = builder.scale(1).asBufferedImage();
            int originalWidth = originalImage.getWidth();
            if (originalWidth > 1280) {
                double scale = (double) 1280 / originalWidth;
                builder.scale(scale)
                       .outputQuality(0.6)
                       .toFile(file);
                logger.info("压缩图片: {}", file.getPath());
            }
        } catch (IOException e) {
            logger.error("处理图片时出错: {} - {}", file.getPath(), e.getMessage());
        }
    }

    /**
     * 计算文件夹大小
     * @param dir
     * @return
     */
    public static long calculateDirectorySize(File dir) {
        long totalSize = 0;
        for (File file : dir.listFiles()) {
            if (file.isDirectory()) {
                totalSize += calculateDirectorySize(file);
            } else {
                totalSize += file.length();
            }
        }
        return totalSize;
    }

    /**
     * 转换文件大小为可读格式
     * @param bytes
     * @return
     */
    public static String formatBytes(long bytes) {
        String[] units = {"B", "KB", "MB", "GB", "TB"};
        int i = 0;
        while (bytes >= 1024 && i < units.length - 1) {
            bytes /= 1024;
            i++;
        }
        return String.format("%.2f %s", bytes / 1.0, units[i]);
    }

    /**
     * 计算文件夹下图片总数量
     * @param dir
     * @return
     */
    public static int countFiles(File dir) {
        int count = 0;
        for (File file : dir.listFiles()) {
            if (file.isDirectory()) {
                count += countFiles(file);
            } else if (file.getName().toLowerCase().endsWith(".jpeg") ||
                    file.getName().toLowerCase().endsWith(".jpg")) {
                count++;
            }
        }
        return count;
    }
}

稳定运行两个月,磁盘空出200G。再也不用手忙脚乱地联系客服,重启服务了。


http://www.kler.cn/a/418099.html

相关文章:

  • 乌班图单机(不访问外网)部署docker和服务的方法
  • Java之链表1
  • EDA软件研发的DevOps平台
  • MySQL 核心基础 | Pandaer杂货铺
  • 云计算的发展历史与未来展望
  • 网络安全之IP伪造
  • 基于深度学习和卷积神经网络的乳腺癌影像自动化诊断系统(PyQt5界面+数据集+训练代码)
  • git如何快速拉取已经提交的mr进行验证
  • 编程语言之Python
  • LeetCode 热题 100_矩阵置零(18_73_中等_C++)(哈希集合;使用原二维数组记录)
  • 计算机光电成像理论基础
  • springboot360志愿服务管理系统--论文(论文+源码)_kaic
  • 鸿蒙修饰符
  • Maven 如何配置忽略单元测试
  • 前段制程(Front-End)和后段制程(Back-End)
  • CAD 文件 批量转为PDF或批量打印
  • Qt支持RKMPP硬解的视频监控系统/性能卓越界面精美/实时性好延迟低/录像存储和回放/云台控制
  • 记一次搞校园网的经历
  • 【leetcode】组合子集 回溯法
  • mysql将一个表的数据插入到另一个表中
  • 【Vue3】从零开始创建一个VUE项目
  • docker安装seata
  • Shell编程之条件语句
  • 如何在 CentOS 6 VPS 上设置和使用 Yum 仓库
  • 【k8s】解决kubelet下载docker私有仓库验证问题
  • P3打卡-pytorch实现天气识别