背景
经常使用 Typora 编辑器写笔记。有时候,粘贴一张图片之后,又觉得不合适,于是直接在编辑器中直接删除图片的引用。但是 Markdown 文件所在的目录中,对应的图片并没有被清除。
于是思索着,不如写一个清理图片的小工具。
思路
- 读取 markdown 文件,利用正则表达式匹配找出所有图片引用。
- 列出 markdown 文件所在目录中的所有图片。
- 比较两者,找出冗余图片,并将其删除。
实现
为了防止误操作导致无法挽回的损失,先将被测试的笔记备份。
引入依赖
新建 Maven Project,引入依赖:
<project>
<properties>
<commons-io.version>2.6</commons-io.version>
<commons-lang3.version>3.9</commons-lang3.version>
<logback-classic.version>1.2.3</logback-classic.version>
</properties>
<dependencies>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>${commons-io.version}</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>${commons-lang3.version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback-classic.version}</version>
</dependency>
</dependencies>
</project>
这里引入三个依赖,其中,Commons IO 用于文件操作,Commons Lang3 用于字符串操作,Logback 用于日志记录。
需要注意的是,如果你打算使用 SLF4J,并不需要在 pom.xml 文件中显式地引入其依赖,因为 logback-classic
中已经包含了相关的依赖。
参考:http://www.slf4j.org/manual.html#projectDep
日志配置文件
日志配置文件 src/main/resources/logback.xml:
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<!-- 定义日志文件的输出路径 -->
<!-- <property name="USER_HOME" value="G:/log" /> -->
<!-- 输出到控制台 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%msg%n</pattern>
</encoder>
</appender>
<!-- 基于大小以及时间的轮转策略 -->
<!-- 参考:http://www.logback.cn/04%E7%AC%AC%E5%9B%9B%E7%AB%A0Appenders.html -->
<appender name="ROLLING" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 要写入文件的名称。如果文件不存在,则新建。 -->
<!-- <file>${USER_HOME}/logback.log</file> -->
<file>logback.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>%d{yyyyMMdd}/logback-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<maxFileSize>100KB</maxFileSize>
<!-- 最多保留多少数量的归档文件,将会异步删除旧的文件。 -->
<maxHistory>30</maxHistory>
<!-- 所有归档文件总的大小。当达到这个大小后,旧的归档文件将会被异步的删除。 -->
<totalSizeCap>1000MB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>%d{HH:mm:ss.SSS} %-5level [%-10thread] %logger - %msg%n</pattern>
</encoder>
</appender>
<!-- 日志输出级别 -->
<root level="info">
<appender-ref ref="ROLLING" />
<appender-ref ref="STDOUT" />
</root>
</configuration>
关于 Logback 日志配置文件的加载路径,可以参考:http://logback.qos.ch/manual/configuration.html#auto_configuration
Logback 英文参考手册:http://logback.qos.ch/manual/index.html
Logback 中文参考手册:http://www.logback.cn/
功能实现
import java.io.File;
import java.io.IOException;
import java.util.HashSet;
import java.util.Scanner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class PictureClear {
final static Logger logger = LoggerFactory.getLogger(PictureClear.class);
public static void main(String[] args) {
logger.info("冗余图片清理工具 V1.0.0");
Scanner scanner = new Scanner(System.in);
String path = null;
try {
logger.info("请输入 Markdown 文件的路径:");
path = scanner.nextLine();
} catch (Exception e) { // 用户可能按下 Ctrl + C 终止程序
logger.info("程序结束!");
scanner.close();
System.exit(0);
}
// 预检查,判断用户输入的文件是否存在
File file = null;
if (StringUtils.isNotBlank(path)) {
file = new File(path.trim());
if (!file.exists()) {
logger.info("Markdown 文件不存在!");
file = null;
}
}
// 读取 Markdown 文件的内容
String content = null;
if (file != null) { // 如果用户输入的文件不存在,跳过此次操作
logger.info("您输入的路径:" + file.getAbsolutePath());
try {
content = FileUtils.readFileToString(file, "UTF-8");
} catch (IOException e) {
logger.error("文件读取异常:", e);
content = null;
}
}
if(content != null) {
// 找出 Markdown 文件中所有图片的引用
String regex = "(!\\[.*\\])(\\(.*\\))"; // 捕获组,匹配类似于 "![*](*)" 的字符串
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(content);
HashSet<String> picturesInMarkdown = new HashSet<String>();
logger.info("Markdown 文件中所引用的图片:");
while (matcher.find()) {
String ref = matcher.group(0);
// 获取图片名称
int beginIndex = ref.indexOf("](") + 2;
int endIndex = ref.length() - 1;
String picture = ref.substring(beginIndex , endIndex);
logger.info(picture);
// 保存图片名称
picturesInMarkdown.add(picture);
}
// 列出 Markdown 文件所在目录中的图片名称
File directory = file.getParentFile();
String[] extensions = { "png", "jpg", "jpeg", "bmp" }; // 图片扩展名
boolean recursive = false; // 不扫描子目录
HashSet<String> picturesInDirectory = new HashSet<String>();
logger.info("Markdown 文件所在目录中包含的图片(冗余标记 x):");
for (File picture : FileUtils.listFiles(directory, extensions , recursive)) {
String name = picture.getName();
picturesInDirectory.add(name);
if (!picturesInMarkdown.contains(name)) {
name = name + " x"; // 标记冗余图片
}
logger.info(name);
}
// 列出冗余图片,并将其删除
picturesInDirectory.removeAll(picturesInMarkdown);
for (String picture : picturesInDirectory) {
logger.info("冗余图片:" + picture);
logger.info("删除这张图片吗?[Y/N/QUIT]:");
String delete = scanner.nextLine();
if (StringUtils.isNotBlank(delete) && delete.trim().equalsIgnoreCase("y")) {
logger.info("删除图片:" + directory.getAbsolutePath() + File.separator + picture);
try {
FileUtils.forceDelete(new File(directory, picture));
} catch (IOException e) {
logger.error("文件删除异常:", e);
}
} else if(delete.trim().equalsIgnoreCase("quit")) {
logger.info("您选择终止所有操作!");
break;
}
}
logger.info("操作完成,程序结束!");
}
scanner.close();
System.exit(0);
}
}
打包项目
要将项目打包成可执行 jar,需要引入 Maven 插件。
<project>
...
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>com.mk.PictureClear</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
第 21 行指定应用程序的入口点。
关于 Maven 插件的用法,参考:
http://maven.apache.org/plugins/maven-shade-plugin/examples/executable-jar.html
http://maven.apache.org/plugins/index.html
执行 Maven install 之后,可以在项目的 target 目录中找到该可执行文件。
运行测试
使用 java -jar
命令运行该 jar 文件,效果如下:
java -jar picture-clear-1.0.0.jar
冗余图片清理工具 V1.0.0
请输入 Markdown 文件的路径:
G:\20191212\Spring Boot 开启 HTTPS\Spring Boot 开启 HTTPS.md
您输入的路径:G:\20191212\Spring Boot 开启 HTTPS\Spring Boot 开启 HTTPS.md
Markdown 文件中所引用的图片:
image-20200119163246754.png
image-20200119165306747.png
image-20200119165424156.png
image-20200119153912075.png
image-20200119170233272.png
Markdown 文件所在目录中包含的图片(冗余标记 x):
image-20200119153912075 - 副本.png x
image-20200119153912075.png
image-20200119163246754 - 副本.png x
image-20200119163246754.png
image-20200119165306747 - 副本.png x
image-20200119165306747.png
image-20200119165424156.png
image-20200119170233272.png
冗余图片:image-20200119165306747 - 副本.png
删除这张图片吗?[Y/N/QUIT]:
y
删除图片:G:\20191212\Spring Boot 开启 HTTPS\image-20200119165306747 - 副本.png
冗余图片:image-20200119163246754 - 副本.png
删除这张图片吗?[Y/N/QUIT]:
y
删除图片:G:\20191212\Spring Boot 开启 HTTPS\image-20200119163246754 - 副本.png
冗余图片:image-20200119153912075 - 副本.png
删除这张图片吗?[Y/N/QUIT]:
y
删除图片:G:\20191212\Spring Boot 开启 HTTPS\image-20200119153912075 - 副本.png
操作完成,程序结束!
相关的操作过程记录在日志文件中,该文件可在 jar 所在的目录中找到: