Spring Boot 自动化脚本-多线程批量压缩图片
Spring Boot 自动化脚本-多线程批量压缩图片
- 支持多线程
- 支持多路径配置
- 支持断点续压
- 支持压缩后文件层级路径不变
- 脚本一键启动,支持本地 main 调用或远程 POST 接口调用
背景:在进行数据迁移时,发现附件文件夹过于庞大,且大都为图片格式,一方面图片数量过多,再一方面,就是在文件上传时,未对图片进行压缩,导致磁盘占用过大。
解决方案:写一个脚本,对服务器图片进行压缩。
目标:压缩后不影响图片内容查看,且压缩后文件结构路径与原来一致。
安装
<dependency><groupId>net.coobird</groupId><artifactId>thumbnailator</artifactId><version>0.4.20</version></dependency>
压缩
Thumbnails.of(inputFile).scale(0.3) //scale是指定图片的大小,值在0到1之间,1就是原图大小.outputQuality(0.3) //图片的质量,值也是在0到1,越接近于1质量越好.toFile(outputFile);
处理逻辑
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import lombok.Data;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import net.coobird.thumbnailator.Thumbnails;import java.io.File;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;/*** 解决图片附件目录过大问题,压缩图片处理* 支持多线程* 支持多路径配置* 支持断点续压* 支持压缩后文件层级路径不变** @author jason*/
@Slf4j
public class ImgReduceService {private static final ExecutorService EXECUTOR_SERVICE = Executors.newFixedThreadPool(20);public static void main(String[] args) {PathInfo pathInfo = new PathInfo();pathInfo.setInputBasePath("/data/attachment");pathInfo.setOutputBasePath("/data/output/attachment");PathInfo pathInfo1 = new PathInfo();pathInfo1.setInputBasePath("/data/attachment2");pathInfo1.setOutputBasePath("/data/output/attachment");ImgReduceService.start(CollectionUtil.newArrayList(pathInfo, pathInfo1));}@SneakyThrowspublic static void start(List<PathInfo> pathInfoList) {for (PathInfo pathInfo : pathInfoList) {String inputBasePath = pathInfo.getInputBasePath();String outputBasePath = pathInfo.getOutputBasePath();if (StrUtil.isBlank(inputBasePath)) {continue;}List<File> fileList = FileUtil.loopFiles(inputBasePath);log.info("文件数量:{}", fileList.size());for (File file : fileList) {String inputFile = FileUtil.getAbsolutePath(file);String inputPath = FileUtil.getAbsolutePath(FileUtil.getParent(file, 1));inputPath = StrUtil.replace(inputPath, "D:", "");inputPath = StrUtil.replace(inputPath, File.separator, "/");String outputPath = StrUtil.replace(inputPath, inputBasePath, "");outputPath = outputBasePath + outputPath;FileUtil.mkdir(outputPath);// 目标文件String outputFile = outputPath + "/" + file.getName();// 已存在的跳过if (FileUtil.exist(outputFile)) {log.info("目标文件已存在:{}", outputFile);continue;}String regex = ".*\\.(jpg|jpeg|png|gif|bmp)$";boolean isImage = ReUtil.isMatch(regex, file.getName());// 图片才处理if (!isImage) {// 非图片,直接免压缩丢过去FileUtil.copy(inputFile, outputPath, false);continue;}// 压缩asyncReduce(inputFile, outputFile, outputPath);}}}/*** 压缩-多线程*/@SneakyThrowsprivate static void asyncReduce(String inputFile, String outputFile, String outputPath) {EXECUTOR_SERVICE.execute(() -> reduce(inputFile, outputFile, outputPath));}/*** 压缩-单线程*/private static void reduce(String inputFile, String outputFile, String outputPath) {try {long startTime = System.currentTimeMillis();Thumbnails.of(inputFile).scale(0.3) //scale是指定图片的大小,值在0到1之间,1就是原图大小.outputQuality(0.3) //图片的质量,值也是在0到1,越接近于1质量越好.toFile(outputFile);log.info("源文件:{}", inputFile);log.info("目标文件:{}", outputFile);log.info("压缩耗时:{}ms", System.currentTimeMillis() - startTime);// long inputSize = FileUtil.size(FileUtil.file(inputFile));
// long outputSize = FileUtil.size(FileUtil.file(outputFile));
// log.info("源文件大小:{},压缩后大小:{}", DataSizeUtil.format(inputSize), DataSizeUtil.format(outputSize));
// double f = (double) inputSize / outputSize;
// log.info("压缩率:{}", NumberUtil.formatPercent(f, 2));} catch (Exception e) {
// log.error("压缩异常", e);log.info("压缩异常:{},源文件路径:{}", e.getMessage(), inputFile);// 压缩失败,直接复制FileUtil.copy(inputFile, outputPath, false);}}/*** 配置信息*/@Datapublic static class PathInfo {/*** 源文件根路径*/private String inputBasePath;/*** 输入文件根路径*/private String outputBasePath;}}
java 简单部署
startup.sh 启动脚本
#!/bin/bashnohup java -Xms2G -Xmx3G -jar job_api.jar > app.log 2>&1 &
shutdown.sh 停止脚本
#!/bin/bash# 应用名称
APP_NAME=job_api# 查找 Java 应用的进程ID
PID=$(ps -ef | grep $APP_NAME | grep -v grep | awk '{print $2}')# 判断是否存在进程ID
if [ -z "$PID" ]; thenecho "未找到名为 $APP_NAME 的进程"
elseecho "正在终止名为 $APP_NAME 的进程,进程ID为:$PID"kill -9 $PID
fi
支持代码调用和接口调用
curl 'http://127.0.0.1:9092/job/index/reduce' \
--header 'Content-Type: application/json' \
--data '
[{"inputBasePath": "/home/env/attachment","outputBasePath": "/home/env/output/attachment"},{"inputBasePath": "/home/env/attachment2","outputBasePath": "/home/env/output/attachment"}
]
'
源码
https://gitee.com/zhaomingjian/workspace_jason_demo/tree/master/spring-boot-thumbnails
相关文章:
Spring Boot 自动化脚本-多线程批量压缩图片
Spring Boot 自动化脚本-多线程批量压缩图片 支持多线程支持多路径配置支持断点续压支持压缩后文件层级路径不变脚本一键启动,支持本地 main 调用或远程 POST 接口调用 背景:在进行数据迁移时,发现附件文件夹过于庞大,且大都为图…...
依托 Spring Boot框架,精铸高扩展性招聘信息管控系统
1 绪 论 1.1 课题背景与意义 在Internet高速发展的今天,计算机的应用几乎完全覆盖我们生活的各个领域,互联网在经济,生活等方面有着举足轻重的地位,成为人们资源共享,信息快速传递的重要渠道。在中国,网上管…...
【前端】理解 JavaScript 对象属性访问的复杂性
博客主页: [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: 前端 文章目录 💯前言💯理论基础:JavaScript 对象属性的访问模式1. 点符号访问(Dot Notation)2. 方括号访问(Bracket Notation)点符号…...
Django异步视图adrf解决办法
提问 在Django编写异步视图的时候会出现 AssertionError: Expected a Response, HttpResponse or HttpStreamingResponse to be returned from the view 或者 TypeError: sync_to_async can only be applied to sync functions. 诸如此类的错误的时候一般发生在异步视图中…...
【一文了解】C#基础-接口
目录 1. 定义 2. 接口的特点与规则 3. 接口的实现 3.1单接口实现 3.2多接口实现 4. 接口的作用和用途 1)扩展行为 2)规范行为 3)降低耦合 5. 接口与继承的比较 1)继承 2)接口 6. 接口与抽象类的比较 1)IComparable(比较器,常用) 2)IComparer(比较器)…...
活着就好20241210
亲爱的朋友们,大家早上好!🌞 今天是10号,星期二,2024年12月的第十天,同时也是第50周的开始,农历甲辰[龙]年十一月初六日。在这晨光熹微的美好时刻,愿那温暖而明媚的阳光轻轻拂过你的…...
多表设计 - 一对一多对多
一.一对一关系概述: 例如:一位用户只能有一张身份证,一张身份证也只能对应一位用户 如果用户基本信息查询频率比用户身份信息查询频率高,为了提高效率,可拆分为两张表: 此时如何体现一对一的关系呢…...
实现 DataGridView 下拉列表功能(C# WinForms)
本文介绍如何在 WinForms 中使用 DataGridViewComboBoxColumn 实现下拉列表功能,并通过事件响应来处理用户的选择。以下是实现步骤和示例代码。 1. 效果展示 该程序的主要功能是展示如何在 DataGridView 中插入下拉列表,并在选择某一项时触发事件。 2.…...
使用Java创建RabbitMQ消息生产者的详细指南
目录 在现代分布式系统中,消息队列是实现异步通信的重要工具。RabbitMQ作为一种流行的开源消息代理,支持多种消息协议,广泛应用于微服务架构和事件驱动的应用程序中。本文将深入探讨如何使用Java创建RabbitMQ的消息生产者,发送消息…...
【LC】160. 相交链表
题目描述: 给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 。 图示两个链表在节点 c1 开始相交: 题目数据 保证 整个链式结构中不存在环。 注意&…...
Spark架构及运行流程
Spark架构图 Driver: 解析用户的应用程序代码,转化为作业(job)。创建SparkContext上下文对象,其负责与资源管理器(ClusterManager)通信,进行资源的申请、任务的分配和监控等。跟踪Executor的执行情况。可通过UI界面查询运行情况。…...
Linux安装Python2.7.5(centos自带同款)
卸载已安装的python,防止版本兼容问题 rpm -qa|grep python|xargs rpm -ev --allmatches --nodeps 删除残余文件 whereis python |xargs rm -frv 安装前提是已安装gcc和g gcc --version g --version 下载安装python2.7.5 https://www.python.org/downloads/release/pyt…...
上传ssh公钥到目标服务器
创建密钥 ssh-keygen -t rsa -b 4096 -C "xxxx.xx"上传 sudo ssh-copy-id -i /Users/xx/.ssh/id_rsa.pub root127.0.0.1...
【LLMs】用LM Studio本地部署离线大语言模型
文章目录 一、下载LM Studio二、下载大语言模型1. 查看模型介绍2. 点击模型文件进行下载2.1 完整下载2.2 部分下载 三、加载模型1. 打开LM Studio图形化界面,点击**My Models**2. 然后,点击“...”,选择“change”,选择刚下载好的…...
SpringBoot下类加入容器的几种方式
SpringBoot下类加入容器的几种方式 在 Spring Boot 中,类加入容器的方式不仅多样,而且每种方式都有其特定的使用场景。以下是几种常见的将类加入 Spring 容器的方法及其适用场景: 1. 使用 Component 及其派生注解 使用场景:当开…...
【Mysql】忘记Root密码后如何不影响数据进行重置密码
方法一:通用方法--启动时跳过权限表 1> 停止数据库 以管理员方式打开cmd!! C:\Users\Administrator>net stop mysql MySQL 服务正在停止.. MySQL 服务已成功停止。 2> 启动时跳过权限表 mysqld --console --skip-grant-tables -…...
宝塔内设置redis后,项目以及RedisDesktopManager客户端连接不上!
项目展现问题: Unable to connect to Redis; nested exception is io.lettuce.core.RedisConnectionException: Unable to connect to xxx.宝塔外链.ip.xxxx:6379 redis客户端连接失败: 1、宝塔中确认redis端口已放行 2、修改redis的配置 bind&#x…...
一文了解模式识别顶会ICPR 2024的研究热点与最新趋势
简介 对模式识别研究领域前沿方向的跟踪是提高科研能力和制定科研战略的关键。本文通过图文并茂的方式介绍了ICPR 2024的研究热点与最新趋势,帮助读者了解和跟踪模式识别的前沿研究方向。本推文的作者是黄星宇,审校为邱雪和许东舟。 一、会议介绍 ICPR…...
【深度学习】深刻理解BERT
BERT(Bidirectional Encoder Representations from Transformers)是由Google于2018年提出的一种预训练的语言表示模型,它基于Transformer架构并能够处理自然语言处理(NLP)中的多种任务。BERT的核心创新是其使用了双向编…...
一种基于通义千问prompt辅助+Qwen2.5-coder-32b+Bolt.new+v0+Cursor的无代码对话网站构建方法
前言 今年似乎大模型之间的“内卷”已经有些偃旗息鼓了,各大技术公司逐渐从单纯追求模型参数量的竞赛中抽身,转向更加注重模型的实际应用效果与效率,开始内卷起了LLM“载具” 不知道这个词是不是我第一个发明的哈,总之我更喜欢…...
Linux应用开发之网络套接字编程(实例篇)
服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …...
java_网络服务相关_gateway_nacos_feign区别联系
1. spring-cloud-starter-gateway 作用:作为微服务架构的网关,统一入口,处理所有外部请求。 核心能力: 路由转发(基于路径、服务名等)过滤器(鉴权、限流、日志、Header 处理)支持负…...
【入坑系列】TiDB 强制索引在不同库下不生效问题
文章目录 背景SQL 优化情况线上SQL运行情况分析怀疑1:执行计划绑定问题?尝试:SHOW WARNINGS 查看警告探索 TiDB 的 USE_INDEX 写法Hint 不生效问题排查解决参考背景 项目中使用 TiDB 数据库,并对 SQL 进行优化了,添加了强制索引。 UAT 环境已经生效,但 PROD 环境强制索…...
【第二十一章 SDIO接口(SDIO)】
第二十一章 SDIO接口 目录 第二十一章 SDIO接口(SDIO) 1 SDIO 主要功能 2 SDIO 总线拓扑 3 SDIO 功能描述 3.1 SDIO 适配器 3.2 SDIOAHB 接口 4 卡功能描述 4.1 卡识别模式 4.2 卡复位 4.3 操作电压范围确认 4.4 卡识别过程 4.5 写数据块 4.6 读数据块 4.7 数据流…...
JVM垃圾回收机制全解析
Java虚拟机(JVM)中的垃圾收集器(Garbage Collector,简称GC)是用于自动管理内存的机制。它负责识别和清除不再被程序使用的对象,从而释放内存空间,避免内存泄漏和内存溢出等问题。垃圾收集器在Ja…...
【android bluetooth 框架分析 04】【bt-framework 层详解 1】【BluetoothProperties介绍】
1. BluetoothProperties介绍 libsysprop/srcs/android/sysprop/BluetoothProperties.sysprop BluetoothProperties.sysprop 是 Android AOSP 中的一种 系统属性定义文件(System Property Definition File),用于声明和管理 Bluetooth 模块相…...
Linux-07 ubuntu 的 chrome 启动不了
文章目录 问题原因解决步骤一、卸载旧版chrome二、重新安装chorme三、启动不了,报错如下四、启动不了,解决如下 总结 问题原因 在应用中可以看到chrome,但是打不开(说明:原来的ubuntu系统出问题了,这个是备用的硬盘&a…...
前端开发面试题总结-JavaScript篇(一)
文章目录 JavaScript高频问答一、作用域与闭包1.什么是闭包(Closure)?闭包有什么应用场景和潜在问题?2.解释 JavaScript 的作用域链(Scope Chain) 二、原型与继承3.原型链是什么?如何实现继承&a…...
MySQL中【正则表达式】用法
MySQL 中正则表达式通过 REGEXP 或 RLIKE 操作符实现(两者等价),用于在 WHERE 子句中进行复杂的字符串模式匹配。以下是核心用法和示例: 一、基础语法 SELECT column_name FROM table_name WHERE column_name REGEXP pattern; …...
浅谈不同二分算法的查找情况
二分算法原理比较简单,但是实际的算法模板却有很多,这一切都源于二分查找问题中的复杂情况和二分算法的边界处理,以下是博主对一些二分算法查找的情况分析。 需要说明的是,以下二分算法都是基于有序序列为升序有序的情况…...
