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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/35419.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

《网络安全》相关知识点总结

第一章 安全现状及趋势 第二章 网络安全概述 2.1 信息保障阶段 信息保障技术框架IATF&#xff1a; 由美国国家安全局制定&#xff0c;提出“纵深防御策略” DiD&#xff08;Defense-in-Depth Strategy&#xff09; 在信息保障的概念下&#xff0c;信息安全保障的PDRR模型的内涵…

DApp浏览器能否集成在自己开发的DApp里?

答案是肯定的。在技术层面&#xff0c;DApp浏览器可以完全集成到你自己开发的DApp中&#xff0c;从而提供一个一体化的用户体验。本文将详细分析如何实现这一目标&#xff0c;以及其中的技术实现、优势和需要注意的问题。 一、什么是DApp浏览器&#xff1f; DApp浏览器是一种支…

MySQL--用户权限

1.使用root用户登录MySQL客户端&#xff0c;创建一个名为userl的用户&#xff0c;初始密码为123456;创建一个名为user2的用户&#xff0c;无初始密码。然后&#xff0c;分别使用uesr1、user2登录MySQL 客户端。 创建两个用户 使用user1登录 使用user2登录 2.使用root用户登录&a…

星海智算:skl-videolingo-2.0(AI视频翻译)使用教程

&#xff08;一&#xff09;项目介绍 VideoLingo是一款专为视频创作者设计的开源自动化工具&#xff0c;旨在提供从视频字幕生成到声音克隆配音的一站式服务。以下是对VideoLingo的详细介绍&#xff1a; 1、核心功能​ 1.1、一键全自动视频搬运​ 支持从YouTube等平台下载视…

SQL靶场第八关攻略

一.判断类型 输入?id1 and 11-- 输入?id1 and 12--页面都正常&#xff0c;说明不是数值型 输入?id1页面没有回显 加上--页面正常&#xff0c;说明是字符型注入 二.判断列数 输入?id1 order by 3--页面正常 输入?id1 order by 4--页面没有回显&#xff0c;说明一共有三列…

华为HCIP-Datacom H12-821H12-831 (12月最新题库)

备考HCIP-datacom的小伙伴注意啦 !!! 2024年下半年12月份最新(H12-821和H12-831)题库带解析,有需要的小伙伴移动至文章末 H12-821: H12-831: 1.BGP 邻居建立过程的状态存在以下几种&#xff1a;那么建立一个成功的连接所经历的状态机顺序是 A、3-1-2-5-4 B、1-3-5-2-4 C、…

Flask使用长连接

Flask使用flask_socketio实现websocket Python中的单例模式 在HTTP通信中&#xff0c;连接复用&#xff08;Connection Reuse&#xff09;是一个重要的概念&#xff0c;它允许客户端和服务器在同一个TCP连接上发送和接收多个HTTP请求/响应&#xff0c;而不是为每个新的请求/响…

MR30分布式 I/O 模块助力 CNC 设备产能飞跃

背景分析 在现代制造业中&#xff0c;CNC 设备扮演着极为关键的角色。然而&#xff0c;CNC 设备在运行过程中也存在着诸多痛点。传统的 CNC 设备往往在控制与通信方面存在局限&#xff0c;其内部的 I/O 系统大多采用集中式架构。这种架构下&#xff0c;一旦需要处理大量的输入输…

远程修改ESXi 6.7管理IP地址

1.启用安全Shell&#xff08;也就是EXSi可以被SSH访问的功能&#xff09; 2.使用SecureCRT SSH2连接ESXi主机&#xff0c;现在使用dcui并没有任何反应&#xff0c;在Session标签栏右键点击Disconnect。 The time and date of this login have been sent to the system logs.WA…

Vulnhub靶场 Kioptrix: Level 1 (#1) 练习

目录 0x00 环境准备0x01 主机信息收集0x02 站点信息收集0x03 漏洞查找与利用1. 方法一&#xff1a;mod_ssl 2.8.42. 方法二&#xff1a;CVE-2003-02013. 方法三&#xff1a;Samba 0x04 总结 0x00 环境准备 下载链接&#xff1a;http://www.kioptrix.com/dlvm/Kioptrix_Level_1.…

消息中间件-Kafka3-kafkaJavaClient小例

消息中间件-Kafka3-kafkaJavaClient小例 Kafak Java Client private static final String KAFKA_TOPIC "kafak-test";private static String bootstrapServers "localhost:9092";private static AdminClient client null;static {Properties config n…

关于光耦合器的常见误解

光耦合器以其提供电气隔离的能力而闻名&#xff0c;广泛应用于从电源到通信系统的各种应用。尽管光耦合器非常普遍&#xff0c;但人们对其特性和用途存在一些常见的误解。本文将揭穿一些最常见的误解&#xff0c;以帮助工程师和爱好者做出更明智的决策。 误解1&#xff1a;光耦…

【简洁明快】使用python读取数据建立pptx (python-pptx图文调整案例)

使用python自动读取数据建立pptx 前言如何使用 Python 自动生成 PPTX第一步&#xff1a;安装所需库第二步&#xff1a;创建一个新的 PPTX第三步&#xff1a;添加幻灯片第四步&#xff1a;添加内容添加文本添加图片第五步&#xff1a;保存 PPTX 图文实操案例&#xff08;自动读取…

【智体OS】官方上新发布rtphone分布式安卓设备远程控制插件:实现远程访问和管理手机

【智体OS】官方上新发布rtphone分布式安卓设备远程控制插件&#xff1a;实现远程访问和管理手机 dtns.network是一款主要由JavaScript编写的智体世界引擎&#xff08;内嵌了three.js编辑器的定制版-支持以第一视角浏览3D场馆&#xff09;&#xff0c;可以在浏览器和node.js、d…

Vue智慧商城项目

创建项目 vue组件库 — vant-ui&#xff08;常用于移动端&#xff09; Vant 2 - 轻量、可靠的移动端组件库 安装vant npm i vantlatest-v2 -S 引入组件 按需导入和全部导入 全部导入 整个组件库的所有组件都导进来&#xff0c;缺点是增加了代码包体积 main.js import…

提升网站流量的关键:AI在SEO关键词优化中的应用

内容概要 在当今数字时代&#xff0c;提升网站流量已成为每个网站管理员的首要任务。而人工智能的技术进步&#xff0c;为搜索引擎优化&#xff08;SEO&#xff09;提供了强有力的支持&#xff0c;尤其是在关键词优化方面。关键词是连接用户需求与网站内容的桥梁&#xff0c;其…

以MP6924A为核心的LLC拓扑学习【一】

PFCLLC: 在PFC&#xff08;功率因数校正&#xff09;和LLC&#xff08;谐振变换器&#xff09;组成的电源系统中&#xff0c;各个电路有特定的作用&#xff0c;它们协同工作以实现高效率和高功率因数的电能转换。 1. PFC&#xff08;功率因数校正&#xff09;电路的作用 PFC电…

实践教程|Transformer Decoder-Only 模型批量生成 Trick

导读 本文给出了一个用单Transformer decoder&#xff08; GPT&#xff09;模型进行批量生成时的解决方法。 发现用单 Transformer decoder &#xff08;Aka GPT&#xff09;模型进行生成时&#xff0c;因为位置对齐等问题&#xff0c;进行批量生成时十分麻烦。 训练时&#…

DevExpress WPF v24.2新功能预览 - 键盘导航和屏幕阅读器功能增强

DevExpress WPF拥有120个控件和库&#xff0c;将帮助您交付满足甚至超出企业需求的高性能业务应用程序。通过DevExpress WPF能创建有着强大互动功能的XAML基础应用程序&#xff0c;这些应用程序专注于当代客户的需求和构建未来新一代支持触摸的解决方案。 无论是Office办公软件…

threejs相机辅助对象cameraHelper

为指定相机创建一个辅助对象&#xff0c;显示这个相机的视锥。 想要在场景里面显示相机的视锥&#xff0c;需要创建两个相机。 举个例子&#xff0c;场景中有个相机A&#xff0c;想要显示相机A的视锥&#xff0c;那么需要一个相机B&#xff0c;把B放在A的后面&#xff0c;两个…