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

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

晚上七点,煤矿调运的物流调度系统突然磁盘报名导致服务崩溃。系统用的是微服务,没有详细操作说明,也不敢动,运煤车辆排起了长队,只能联系厂家处理。好在经过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。再也不用手忙脚乱地联系客服,重启服务了。

相关文章:

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

晚上七点&#xff0c;煤矿调运的物流调度系统突然磁盘报名导致服务崩溃。系统用的是微服务&#xff0c;没有详细操作说明&#xff0c;也不敢动&#xff0c;运煤车辆排起了长队&#xff0c;只能联系厂家处理。好在经过30多分钟的处理&#xff0c;服务终于启动&#xff0c;系统运…...

计算机网络基础篇

TCP/IP网络模型 TCP/IP网络模型的作用就是给数据包进行层层封装&#xff0c;帮助数据包能够正确的找到对应的设备接受数据。 一个URL所经历的全部过程 URL所经历的全部过程&#xff1a; HTTP -> DNS ->协议栈-TCP->IP->MAC->网卡->交换机->路由器->服…...

32 从前序与中序遍历序列构造二叉树

32 从前序与中序遍历序列构造二叉树 32.1 从前序与中序遍历序列构造二叉树解决方案 class Solution { public:TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {return buildTreeHelper(preorder, inorder, 0, 0, inorder.size() - 1)…...

D82【python 接口自动化学习】- pytest基础用法

day82 pytest初体验 学习日期&#xff1a;20241128 学习目标&#xff1a;pytest基础用法 -- pytest初体验 学习笔记&#xff1a; 文件命名规范 py测试文件必须以test_开头&#xff08;或_test结尾&#xff09;测试方法必须以test开头测试类必须以Test开头&#xff0c;并且…...

在开发环境中,前端(手机端),后端(电脑端),那么应该如何设置iisExpress

首先&#xff0c;要想手机端应用能成功请求后端&#xff0c;两个设备至少需在同一个局域网内&#xff0c;且IP地址互通&#xff1b; 因为ajax是http(s)://IP地址端口号的方式请求&#xff0c;但是iisExpress默认是localhost如何解决&#xff0c;并没有IP地址&#xff0c;所以手…...

磁盘/系统空间占满导致黑屏死机无法开机的解决办法

文章目录 起因具体操作1.重启虚拟机&#xff0c;一直按CtrlShitf进入GRUP界面2.选“Ubuntu高级选项”并回车选择第二个&#xff0c;recovery mode![请添加图片描述](https://i-blog.csdnimg.cn/direct/201f9784c203406d802d24b39dc2d4a3.png)3.4.命令查看磁盘情况5.查找和删除文…...

使用zabbix监控k8s

一、 参考文献 小阿轩yx-案例&#xff1a;Zabbix监控kubernetes云原生环境 手把手教你实现zabbix对Kubernetes的监控 二、部署经验 关于zabbix监控k8s&#xff0c;总体来说是分为两块内容&#xff0c;一是在k8s集群部署zabbix-agent和zabbix- proxy。二是在zabbix进行配置。…...

MacOS安装MySQL数据库和Java环境以及Navicat

安装MySQL 去官网下载&#xff1a;MySQL 下载好后安装&#xff0c;在设置里往下滑&#xff0c;出现了这样&#xff0c;就代表安装成功了 接下来配置环境&#xff1a; 首先在我们的设备上找到终端并打开,输入 vim ~/.bash_profile(注意vim后面的空格)&#xff0c;输入完成后点击…...

算法的复杂度

1.数据结构前言 下面的概念有的比较难理解&#xff0c;做个了结就行。 1.1数据结构的起源 在现实生活中我们更多地并不是解决数值计算的问题&#xff0c;而是 需要一些更科学的手段如&#xff08;表&#xff0c;数&#xff0c;图等数据结构&#xff09;&#xff0c;才能更好…...

Linux命令进阶·如何切换root以及回退、sudo命令、用户/用户组管理,以及解决创建用户不显示问题和Ubuntu不显示用户名只显示“$“符号问题

目录 1. root用户&#xff08;超级管理员&#xff09; 1.1 用于账户切换的系统命令——su 1.2 退回上一个用户命令——exit 1.3 普通命令临时授权root身份执行——sudo 1.3.1 为普通用户配置sudo认证 2. 用户/用户组管理 2.1 用户组管理 2.2 用户管理 2.2.1 …...

若依项目源码阅读

源码阅读 前端代码分析 代码生成器生成的前端代码有两个&#xff0c;分别是course.js用于向后端发送ajax请求的接口代码&#xff0c;另一个是index.vue&#xff0c;用于在浏览器展示课程管理的视图组件。前端的代码是基于vue3elementplus。 template用于展示前端组件别的标签…...

JVM知识点学习-1

学习视频&#xff1a;狂神说Java 类加载器和双亲委派机制 类加载器 作用&#xff1a;加载Class文件 流程&#xff1a;这里的名字car1。。在栈里面&#xff0c;但是数据在堆里面 类加载器的几个类型&#xff1a; 虚拟机自带的类加载器&#xff1b;启动类&#xff08;根Boot…...

TypeScript和JavaScript区别详解

文章目录 TypeScript和JavaScript区别详解一、引言二、类型系统1、静态类型检查TypeScript 示例JavaScript 示例 2、类型推断TypeScript 示例JavaScript 示例 三、面向对象编程TypeScript 示例JavaScript 示例 四、使用示例1. 环境搭建2. 创建TypeScript项目3. 安装TypeScript插…...

RVO动态避障技术方案介绍

原文&#xff1a;RVO动态避障技术方案介绍 - 哔哩哔哩 我们在开发游戏的时候经常会遇到这样的问题&#xff0c;当我们寻路的时候&#xff0c;其它人也在寻路&#xff0c;如何避免不从其它人的位置穿过。这个叫做动态避障&#xff0c;目前主流的解决方案就是RVO。本节我们来介绍…...

Vue进阶之单组件开发与组件通信

书接上篇&#xff0c;我们了解了如何快速创建一个脚手架&#xff0c;现在我们来学习如何基于vite创建属于自己的脚手架。在创建一个新的组件时&#xff0c;要在新建文件夹中打开终端创建一个基本的脚手架&#xff0c;可在脚手架中原有的文件中修改或在相应路径重新创建&#xf…...

OGRE 3D----5. OGRE和QML事件交互

在现代图形应用程序开发中,OGRE(Object-Oriented Graphics Rendering Engine)作为一个高性能的3D渲染引擎,广泛应用于游戏开发、虚拟现实和仿真等领域。而QML(Qt Modeling Language)则是Qt框架中的一种声明式语言,专注于设计用户界面。将OGRE与QML结合,可以充分利用OGR…...

ARIMA-神经网络混合模型在时间序列预测中的应用

ARIMA-神经网络混合模型在时间序列预测中的应用 1. 引言 1.1 研究背景与意义 时间序列预测在现代数据科学中扮演着越来越重要的角色。从金融市场的价格走势到工业生产的需求预测,从气象数据的天气预报到用电量的负荷预测,时间序列分析无处不在。传统的统计方法和现代深度学习…...

常见靶场的搭建

漏洞靶场 渗透测试&#xff08;漏洞挖掘&#xff09;切忌纸上谈兵&#xff0c;学习渗透测试&#xff08;漏洞挖掘&#xff09;知识的过程中&#xff0c;我们通常需要一个包含漏洞的测试环境来进行训练。而在非授权情况下&#xff0c;对于网站进行渗透测试攻击&#xff0c;是触及…...

[MacOS] [kubernetes] MacOS玩转虚拟化最佳实践

❓ 为什么不在MacOS本机安装呢&#xff1f;因为M系列芯片是Arm架构&#xff0c;与生产环境或者在本地调试时候&#xff0c;安装虚拟镜像和X86不同&#xff0c;造成不必要的切换环境的额外成本&#xff0c;所以在虚拟化的x86调试 步骤 & 详情 一: 安装OrbStack & 并配置…...

HarmonyOS:@Provide装饰器和@Consume装饰器:与后代组件双向同步

一、前言 Provide和Consume&#xff0c;应用于与后代组件的双向数据同步&#xff0c;应用于状态数据在多个层级之间传递的场景。不同于上文提到的父子组件之间通过命名参数机制传递&#xff0c;Provide和Consume摆脱参数传递机制的束缚&#xff0c;实现跨层级传递。 其中Provi…...

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…...

关于nvm与node.js

1 安装nvm 安装过程中手动修改 nvm的安装路径&#xff0c; 以及修改 通过nvm安装node后正在使用的node的存放目录【这句话可能难以理解&#xff0c;但接着往下看你就了然了】 2 修改nvm中settings.txt文件配置 nvm安装成功后&#xff0c;通常在该文件中会出现以下配置&…...

Android 之 kotlin 语言学习笔记三(Kotlin-Java 互操作)

参考官方文档&#xff1a;https://developer.android.google.cn/kotlin/interop?hlzh-cn 一、Java&#xff08;供 Kotlin 使用&#xff09; 1、不得使用硬关键字 不要使用 Kotlin 的任何硬关键字作为方法的名称 或字段。允许使用 Kotlin 的软关键字、修饰符关键字和特殊标识…...

Aspose.PDF 限制绕过方案:Java 字节码技术实战分享(仅供学习)

Aspose.PDF 限制绕过方案&#xff1a;Java 字节码技术实战分享&#xff08;仅供学习&#xff09; 一、Aspose.PDF 简介二、说明&#xff08;⚠️仅供学习与研究使用&#xff09;三、技术流程总览四、准备工作1. 下载 Jar 包2. Maven 项目依赖配置 五、字节码修改实现代码&#…...

LINUX 69 FTP 客服管理系统 man 5 /etc/vsftpd/vsftpd.conf

FTP 客服管理系统 实现kefu123登录&#xff0c;不允许匿名访问&#xff0c;kefu只能访问/data/kefu目录&#xff0c;不能查看其他目录 创建账号密码 useradd kefu echo 123|passwd -stdin kefu [rootcode caozx26420]# echo 123|passwd --stdin kefu 更改用户 kefu 的密码…...

iview框架主题色的应用

1.下载 less要使用3.0.0以下的版本 npm install less2.7.3 npm install less-loader4.0.52./src/config/theme.js文件 module.exports {yellow: {theme-color: #FDCE04},blue: {theme-color: #547CE7} }在sass中使用theme配置的颜色主题&#xff0c;无需引入&#xff0c;直接可…...

宇树科技,改名了!

提到国内具身智能和机器人领域的代表企业&#xff0c;那宇树科技&#xff08;Unitree&#xff09;必须名列其榜。 最近&#xff0c;宇树科技的一项新变动消息在业界引发了不少关注和讨论&#xff0c;即&#xff1a; 宇树向其合作伙伴发布了一封公司名称变更函称&#xff0c;因…...

FFmpeg:Windows系统小白安装及其使用

一、安装 1.访问官网 Download FFmpeg 2.点击版本目录 3.选择版本点击安装 注意这里选择的是【release buids】&#xff0c;注意左上角标题 例如我安装在目录 F:\FFmpeg 4.解压 5.添加环境变量 把你解压后的bin目录&#xff08;即exe所在文件夹&#xff09;加入系统变量…...

Linux系统部署KES

1、安装准备 1.版本说明V008R006C009B0014 V008&#xff1a;是version产品的大版本。 R006&#xff1a;是release产品特性版本。 C009&#xff1a;是通用版 B0014&#xff1a;是build开发过程中的构建版本2.硬件要求 #安全版和企业版 内存&#xff1a;1GB 以上 硬盘&#xf…...

深入理解Optional:处理空指针异常

1. 使用Optional处理可能为空的集合 在Java开发中&#xff0c;集合判空是一个常见但容易出错的场景。传统方式虽然可行&#xff0c;但存在一些潜在问题&#xff1a; // 传统判空方式 if (!CollectionUtils.isEmpty(userInfoList)) {for (UserInfo userInfo : userInfoList) {…...