typora 瘦身
typora 瘦身
1、参考资料
- https://blog.csdn.net/zha\_ojunchen/article/details/107029846
2、Why?
- 当使用如下设置时,复制黏贴的图片会自动保存在这个 {filename}.assets 文件夹中
- 问题来了,复制文件,图片会自动保存到 assets 文件夹,并在 Markdown 文件产生该图片链接语法:
![]()
- 但是删除、修改 Markdown 中的图片链接语句,并不会删除 assets 文件夹中的图片,这导致了经常assets 文件夹会产生很大的无用图片,感觉这是是 Markdown 存储的一个痛点
3、目标
- 编写 Java 代码,对 Typora 进行瘦身,删除本地无用的图片
4、代码
大致思路:使用递归遍历整个笔记根目录
- 如果遇到 MD 文件,则使用正则表达式匹配文中所用到的图片标签:
![]()
,进而获取文中所引用的图片名称 - 我们将文中所引用的图片名称和本地图片名称做差集,便能得到无用的图片,删除即可
public class TyporaClean {
- 如果遇到 MD 文件,则使用正则表达式匹配文中所用到的图片标签:
public static void main(String[] args) {
// 笔记存储根目录
String noteRootPath;
// 从命令行读取笔记存储根目录,否则使用默认值
if (args == null || args.length == 0) {
noteRootPath = "D:\\我的坚果云";
// noteRootPath = "C:\\Users\\Heygo\\Desktop\\Debug";
} else {
noteRootPath = args[0];
}
// 执行 Typora 瘦身程序
doClean(noteRootPath);
}
/**
* 执行 Typora 瘦身程序
* @param destPath 笔记存储根目录
*/
private static void doClean(String destPath) {
// 获取当前路径的File对象
File destPathFile = new File(destPath);
// 获取当前路径下所有的子文件和路径
File[] allFiles = destPathFile.listFiles();
// 遍历allFiles
for (File curFile : allFiles) {
// 获取curFile对象是否为文件夹
Boolean isDirectory = curFile.isDirectory();
// 如果是文件夹
if (isDirectory) {
// 获取当前curFile对象对应的绝对路径名
String absolutePath = curFile.getAbsolutePath();
// 如果是asset文件夹,则直接调过
if (absolutePath.endsWith(".assets")) {
continue;
}
// 如果是文件夹,则继续执行递归
doClean(absolutePath);
} else {
// 如果是文件,执行单个md文件的图片瘦身
doSinglePicClean(curFile);
}
}
}
/**
* 执行单个md文件的图片瘦身
* @param curFile MD文件的File对象
*/
public static void doSinglePicClean(File curFile) {
// 获取文件名
String curFileName = curFile.getName();
// 文件名后缀,我们只处理md文件
Boolean isMd = curFileName.endsWith(".md");
// 如果不是md文件,我们不处理
if (!isMd) {
return;
}
// 存储图片的文件夹名称
String curFilNameWithoutMd = curFileName.replace(".md", "");
String curAssetName = curFilNameWithoutMd + ".assets";
// 创建asset文件夹所对应的file对象
String curAssetAbsolutePath = curFile.getParent() + "\\" + curAssetName;
File curAssetFile = new File(curAssetAbsolutePath);
// 判断 assets文件夹是否存在,
Boolean iscurAssetExist = curAssetFile.exists();
// assets文件夹存在才执行清理操作
if (iscurAssetExist) {
//asset文件夹存在,则进行瘦身
CleanUnnecessaryPic(curFile, curAssetFile);
}
}
/**
* 删除无用图片
* @param curFile MD文件的File对象
* @param curAssetFile MD文件对应的assets目录的File对象
*/
public static void CleanUnnecessaryPic(File curFile, File curAssetFile) {
// 获取所有图片的绝对路径
File[] allPicFiles = curAssetFile.listFiles();
// 获取md文件中用到的所有图片
String[] usedPicNames = getUsedPicNames(curFile);
//对比,并进行删除
CleanUnusedPic(allPicFiles, usedPicNames);
}
/**
* 取md文件中用到的所有图片
* @param curFile MD文件的File对象
* @return
*/
public static String[] getUsedPicNames(File curFile) {
// 读取md文件内容
String mdFileContent = readMdFileContent(curFile);
// 图片名称
// 图片路径存储格式:
/*
\[.*\]:[image-20200603100128164]
. :匹配任意字符
* :出现0次或多次
\(.+\):(IDEA快捷键.assets/image-20200603100128164.png)
. :匹配任意字符
+ :出现1次或多次
*/
String regex = "!\\[.*\\]\\(.+\\)";
// 匹配文章中所有的图片标签
Matcher matcher = Pattern.compile(regex).matcher(mdFileContent);
// imageNames 用于存储匹配到的图片标签
List<String> imageNames = new ArrayList<>();
//遍历匹配项,将其添加至集合中
while (matcher.find()) {
// 得到当前图片标签
String curImageLabel = matcher.group();
// 放心大胆地使用"/"截取子串,因为文件名不能包含"/"字符
Integer picNameStartIndex = curImageLabel.lastIndexOf("/") + 1;
Integer picNameEndIndex = curImageLabel.length() - 1;
// 得到图片名称
String curImageName = curImageLabel.substring(picNameStartIndex, picNameEndIndex);
// 添加至集合中
imageNames.add(curImageName);
}
// 转换为数组返回
String[] retStrs = new String[imageNames.size()];
return imageNames.toArray(retStrs);
}
/**
* 读取MD文件的内容
* @param curFile MD文件的File对象
* @return
*/
public static String readMdFileContent(File curFile) {
// 存储md文件内容
StringBuilder sb = new StringBuilder();
// 当前行内容
String curLine;
// 装饰者模式:FileReader无法一行一行读取,所以使用BufferedReader装饰FileReader
try (
FileReader fr = new FileReader(curFile);
BufferedReader br = new BufferedReader(fr);
) {
// 当前行有内容
while ((curLine = br.readLine()) != null) {
sb.append(curLine + "\r\n");
}
} catch (IOException e) {
e.printStackTrace();
}
// 返回md文件内容
return sb.toString();
}
/**
* 清除无用的图片
* @param allPicFiles 所有本地图片的File对象数组
* @param usedPicNames MD文件中使用到的图片名称数组
*/
private static void CleanUnusedPic(File[] allPicFiles, String[] usedPicNames) {
// assets文件夹中如果没有图片,则直接返回
if (allPicFiles == null || allPicFiles.length == 0) {
return;
}
// 获取asset文件夹的绝对路径
String assetPath = allPicFiles[0].getParent();
// 为了便于操作,将数组转换为List
List<String> usedPicNameList = Arrays.asList(usedPicNames);
// 遍历所有本地图片,看看有哪些图片没有被使用
for (File curPicFile : allPicFiles) {
// 如果没有被使用,则添加至unusedPicNames集合
String curFileName = curPicFile.getName();
boolean isUsed = usedPicNameList.contains(curFileName);
if (!isUsed) {
// 创建File对象,用于删除
String curPicAbsolutePath = curPicFile.getAbsolutePath();
// 测试用:打印输出
System.out.println("已删除无用图片:" + curPicAbsolutePath);
// 删除文件,看看回收站还有没有,并没有。。。
curPicFile.delete();
}
}
}
}
5、效果
- 程序控制台输出的日志:
6、警告
- 谨慎使用,图片一旦删除无法找回,回收站都没得。。。
还没有评论,来说两句吧...