小程序解决大问题-物流系统磁盘爆满问题处理
晚上七点,煤矿调运的物流调度系统突然磁盘报名导致服务崩溃。系统用的是微服务,没有详细操作说明,也不敢动,运煤车辆排起了长队,只能联系厂家处理。好在经过30多分钟的处理,服务终于启动,系统运行正常。经过排查是一台后端服务器存储了车辆图片,导致磁盘满了,数据库停止运行。以前也有类似的问题,每次都会影响半个小时到一个小时。
作为程序员,分析了该服务器存储的图片都是抓取的原始图片,每张都在3-5M。十几万张图片如果压缩到几百k,磁盘容量问题就应该解决了。跟厂家沟通了下,图片只是最近七天的有用,而且大部分都在一天内已经传到第三方平台,可以压缩。
设计方案:
- 遍历 d://ftp,下面的文件及文件夹,找出 所有jpeg图片;
- 使用thumbnailator将图片压缩,并替换源文件;
- 计算压缩前后的文件夹大小;
- 计算压缩比例;
- 记录处理日志;
- 写入 windows 任务计划程序;
- 使用多线程,考虑生产服务器,严格限制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。再也不用手忙脚乱地联系客服,重启服务了。
相关文章:
小程序解决大问题-物流系统磁盘爆满问题处理
晚上七点,煤矿调运的物流调度系统突然磁盘报名导致服务崩溃。系统用的是微服务,没有详细操作说明,也不敢动,运煤车辆排起了长队,只能联系厂家处理。好在经过30多分钟的处理,服务终于启动,系统运…...
计算机网络基础篇
TCP/IP网络模型 TCP/IP网络模型的作用就是给数据包进行层层封装,帮助数据包能够正确的找到对应的设备接受数据。 一个URL所经历的全部过程 URL所经历的全部过程: 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初体验 学习日期:20241128 学习目标:pytest基础用法 -- pytest初体验 学习笔记: 文件命名规范 py测试文件必须以test_开头(或_test结尾)测试方法必须以test开头测试类必须以Test开头,并且…...
在开发环境中,前端(手机端),后端(电脑端),那么应该如何设置iisExpress
首先,要想手机端应用能成功请求后端,两个设备至少需在同一个局域网内,且IP地址互通; 因为ajax是http(s)://IP地址端口号的方式请求,但是iisExpress默认是localhost如何解决,并没有IP地址,所以手…...
磁盘/系统空间占满导致黑屏死机无法开机的解决办法
文章目录 起因具体操作1.重启虚拟机,一直按CtrlShitf进入GRUP界面2.选“Ubuntu高级选项”并回车选择第二个,recovery mode3.4.命令查看磁盘情况5.查找和删除文…...
使用zabbix监控k8s
一、 参考文献 小阿轩yx-案例:Zabbix监控kubernetes云原生环境 手把手教你实现zabbix对Kubernetes的监控 二、部署经验 关于zabbix监控k8s,总体来说是分为两块内容,一是在k8s集群部署zabbix-agent和zabbix- proxy。二是在zabbix进行配置。…...
MacOS安装MySQL数据库和Java环境以及Navicat
安装MySQL 去官网下载:MySQL 下载好后安装,在设置里往下滑,出现了这样,就代表安装成功了 接下来配置环境: 首先在我们的设备上找到终端并打开,输入 vim ~/.bash_profile(注意vim后面的空格),输入完成后点击…...
算法的复杂度
1.数据结构前言 下面的概念有的比较难理解,做个了结就行。 1.1数据结构的起源 在现实生活中我们更多地并不是解决数值计算的问题,而是 需要一些更科学的手段如(表,数,图等数据结构),才能更好…...
Linux命令进阶·如何切换root以及回退、sudo命令、用户/用户组管理,以及解决创建用户不显示问题和Ubuntu不显示用户名只显示“$“符号问题
目录 1. root用户(超级管理员) 1.1 用于账户切换的系统命令——su 1.2 退回上一个用户命令——exit 1.3 普通命令临时授权root身份执行——sudo 1.3.1 为普通用户配置sudo认证 2. 用户/用户组管理 2.1 用户组管理 2.2 用户管理 2.2.1 …...
若依项目源码阅读
源码阅读 前端代码分析 代码生成器生成的前端代码有两个,分别是course.js用于向后端发送ajax请求的接口代码,另一个是index.vue,用于在浏览器展示课程管理的视图组件。前端的代码是基于vue3elementplus。 template用于展示前端组件别的标签…...
JVM知识点学习-1
学习视频:狂神说Java 类加载器和双亲委派机制 类加载器 作用:加载Class文件 流程:这里的名字car1。。在栈里面,但是数据在堆里面 类加载器的几个类型: 虚拟机自带的类加载器;启动类(根Boot…...
TypeScript和JavaScript区别详解
文章目录 TypeScript和JavaScript区别详解一、引言二、类型系统1、静态类型检查TypeScript 示例JavaScript 示例 2、类型推断TypeScript 示例JavaScript 示例 三、面向对象编程TypeScript 示例JavaScript 示例 四、使用示例1. 环境搭建2. 创建TypeScript项目3. 安装TypeScript插…...
RVO动态避障技术方案介绍
原文:RVO动态避障技术方案介绍 - 哔哩哔哩 我们在开发游戏的时候经常会遇到这样的问题,当我们寻路的时候,其它人也在寻路,如何避免不从其它人的位置穿过。这个叫做动态避障,目前主流的解决方案就是RVO。本节我们来介绍…...
Vue进阶之单组件开发与组件通信
书接上篇,我们了解了如何快速创建一个脚手架,现在我们来学习如何基于vite创建属于自己的脚手架。在创建一个新的组件时,要在新建文件夹中打开终端创建一个基本的脚手架,可在脚手架中原有的文件中修改或在相应路径重新创建…...
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 研究背景与意义 时间序列预测在现代数据科学中扮演着越来越重要的角色。从金融市场的价格走势到工业生产的需求预测,从气象数据的天气预报到用电量的负荷预测,时间序列分析无处不在。传统的统计方法和现代深度学习…...
常见靶场的搭建
漏洞靶场 渗透测试(漏洞挖掘)切忌纸上谈兵,学习渗透测试(漏洞挖掘)知识的过程中,我们通常需要一个包含漏洞的测试环境来进行训练。而在非授权情况下,对于网站进行渗透测试攻击,是触及…...
[MacOS] [kubernetes] MacOS玩转虚拟化最佳实践
❓ 为什么不在MacOS本机安装呢?因为M系列芯片是Arm架构,与生产环境或者在本地调试时候,安装虚拟镜像和X86不同,造成不必要的切换环境的额外成本,所以在虚拟化的x86调试 步骤 & 详情 一: 安装OrbStack & 并配置…...
HarmonyOS:@Provide装饰器和@Consume装饰器:与后代组件双向同步
一、前言 Provide和Consume,应用于与后代组件的双向数据同步,应用于状态数据在多个层级之间传递的场景。不同于上文提到的父子组件之间通过命名参数机制传递,Provide和Consume摆脱参数传递机制的束缚,实现跨层级传递。 其中Provi…...
利用最小二乘法找圆心和半径
#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …...
第19节 Node.js Express 框架
Express 是一个为Node.js设计的web开发框架,它基于nodejs平台。 Express 简介 Express是一个简洁而灵活的node.js Web应用框架, 提供了一系列强大特性帮助你创建各种Web应用,和丰富的HTTP工具。 使用Express可以快速地搭建一个完整功能的网站。 Expre…...
Java 语言特性(面试系列2)
一、SQL 基础 1. 复杂查询 (1)连接查询(JOIN) 内连接(INNER JOIN):返回两表匹配的记录。 SELECT e.name, d.dept_name FROM employees e INNER JOIN departments d ON e.dept_id d.dept_id; 左…...
python/java环境配置
环境变量放一起 python: 1.首先下载Python Python下载地址:Download Python | Python.org downloads ---windows -- 64 2.安装Python 下面两个,然后自定义,全选 可以把前4个选上 3.环境配置 1)搜高级系统设置 2…...
java 实现excel文件转pdf | 无水印 | 无限制
文章目录 目录 文章目录 前言 1.项目远程仓库配置 2.pom文件引入相关依赖 3.代码破解 二、Excel转PDF 1.代码实现 2.Aspose.License.xml 授权文件 总结 前言 java处理excel转pdf一直没找到什么好用的免费jar包工具,自己手写的难度,恐怕高级程序员花费一年的事件,也…...
(二)TensorRT-LLM | 模型导出(v0.20.0rc3)
0. 概述 上一节 对安装和使用有个基本介绍。根据这个 issue 的描述,后续 TensorRT-LLM 团队可能更专注于更新和维护 pytorch backend。但 tensorrt backend 作为先前一直开发的工作,其中包含了大量可以学习的地方。本文主要看看它导出模型的部分&#x…...
376. Wiggle Subsequence
376. Wiggle Subsequence 代码 class Solution { public:int wiggleMaxLength(vector<int>& nums) {int n nums.size();int res 1;int prediff 0;int curdiff 0;for(int i 0;i < n-1;i){curdiff nums[i1] - nums[i];if( (prediff > 0 && curdif…...
Python爬虫(一):爬虫伪装
一、网站防爬机制概述 在当今互联网环境中,具有一定规模或盈利性质的网站几乎都实施了各种防爬措施。这些措施主要分为两大类: 身份验证机制:直接将未经授权的爬虫阻挡在外反爬技术体系:通过各种技术手段增加爬虫获取数据的难度…...
自然语言处理——循环神经网络
自然语言处理——循环神经网络 循环神经网络应用到基于机器学习的自然语言处理任务序列到类别同步的序列到序列模式异步的序列到序列模式 参数学习和长程依赖问题基于门控的循环神经网络门控循环单元(GRU)长短期记忆神经网络(LSTM)…...
【C++】纯虚函数类外可以写实现吗?
1. 答案 先说答案,可以。 2.代码测试 .h头文件 #include <iostream> #include <string>// 抽象基类 class AbstractBase { public:AbstractBase() default;virtual ~AbstractBase() default; // 默认析构函数public:virtual int PureVirtualFunct…...
