typora 瘦身

忘是亡心i 2023-02-25 13:26 108阅读 0赞

typora 瘦身

1、参考资料

  • https://blog.csdn.net/zha\_ojunchen/article/details/107029846

2、Why?

  • 当使用如下设置时,复制黏贴的图片会自动保存在这个 {filename}.assets 文件夹中
  • 问题来了,复制文件,图片会自动保存到 assets 文件夹,并在 Markdown 文件产生该图片链接语法![]()
  • 但是删除、修改 Markdown 中的图片链接语句,并不会删除 assets 文件夹中的图片,这导致了经常assets 文件夹会产生很大的无用图片,感觉这是是 Markdown 存储的一个痛点

image-20200712192154865

3、目标

  • 编写 Java 代码,对 Typora 进行瘦身,删除本地无用的图片

4、代码

  • 大致思路:使用递归遍历整个笔记根目录

    • 如果遇到 MD 文件,则使用正则表达式匹配文中所用到的图片标签:![](),进而获取文中所引用的图片名称
    • 我们将文中所引用的图片名称和本地图片名称做差集,便能得到无用的图片,删除即可

    public class TyporaClean {

  1. public static void main(String[] args) {
  2. // 笔记存储根目录
  3. String noteRootPath;
  4. // 从命令行读取笔记存储根目录,否则使用默认值
  5. if (args == null || args.length == 0) {
  6. noteRootPath = "D:\\我的坚果云";
  7. // noteRootPath = "C:\\Users\\Heygo\\Desktop\\Debug";
  8. } else {
  9. noteRootPath = args[0];
  10. }
  11. // 执行 Typora 瘦身程序
  12. doClean(noteRootPath);
  13. }
  14. /**
  15. * 执行 Typora 瘦身程序
  16. * @param destPath 笔记存储根目录
  17. */
  18. private static void doClean(String destPath) {
  19. // 获取当前路径的File对象
  20. File destPathFile = new File(destPath);
  21. // 获取当前路径下所有的子文件和路径
  22. File[] allFiles = destPathFile.listFiles();
  23. // 遍历allFiles
  24. for (File curFile : allFiles) {
  25. // 获取curFile对象是否为文件夹
  26. Boolean isDirectory = curFile.isDirectory();
  27. // 如果是文件夹
  28. if (isDirectory) {
  29. // 获取当前curFile对象对应的绝对路径名
  30. String absolutePath = curFile.getAbsolutePath();
  31. // 如果是asset文件夹,则直接调过
  32. if (absolutePath.endsWith(".assets")) {
  33. continue;
  34. }
  35. // 如果是文件夹,则继续执行递归
  36. doClean(absolutePath);
  37. } else {
  38. // 如果是文件,执行单个md文件的图片瘦身
  39. doSinglePicClean(curFile);
  40. }
  41. }
  42. }
  43. /**
  44. * 执行单个md文件的图片瘦身
  45. * @param curFile MD文件的File对象
  46. */
  47. public static void doSinglePicClean(File curFile) {
  48. // 获取文件名
  49. String curFileName = curFile.getName();
  50. // 文件名后缀,我们只处理md文件
  51. Boolean isMd = curFileName.endsWith(".md");
  52. // 如果不是md文件,我们不处理
  53. if (!isMd) {
  54. return;
  55. }
  56. // 存储图片的文件夹名称
  57. String curFilNameWithoutMd = curFileName.replace(".md", "");
  58. String curAssetName = curFilNameWithoutMd + ".assets";
  59. // 创建asset文件夹所对应的file对象
  60. String curAssetAbsolutePath = curFile.getParent() + "\\" + curAssetName;
  61. File curAssetFile = new File(curAssetAbsolutePath);
  62. // 判断 assets文件夹是否存在,
  63. Boolean iscurAssetExist = curAssetFile.exists();
  64. // assets文件夹存在才执行清理操作
  65. if (iscurAssetExist) {
  66. //asset文件夹存在,则进行瘦身
  67. CleanUnnecessaryPic(curFile, curAssetFile);
  68. }
  69. }
  70. /**
  71. * 删除无用图片
  72. * @param curFile MD文件的File对象
  73. * @param curAssetFile MD文件对应的assets目录的File对象
  74. */
  75. public static void CleanUnnecessaryPic(File curFile, File curAssetFile) {
  76. // 获取所有图片的绝对路径
  77. File[] allPicFiles = curAssetFile.listFiles();
  78. // 获取md文件中用到的所有图片
  79. String[] usedPicNames = getUsedPicNames(curFile);
  80. //对比,并进行删除
  81. CleanUnusedPic(allPicFiles, usedPicNames);
  82. }
  83. /**
  84. * 取md文件中用到的所有图片
  85. * @param curFile MD文件的File对象
  86. * @return
  87. */
  88. public static String[] getUsedPicNames(File curFile) {
  89. // 读取md文件内容
  90. String mdFileContent = readMdFileContent(curFile);
  91. // 图片名称
  92. // 图片路径存储格式:![image-20200603100128164](Typora 瘦身.assets/image-20200603100128164.png)
  93. /*
  94. \[.*\]:[image-20200603100128164]
  95. . :匹配任意字符
  96. * :出现0次或多次
  97. \(.+\):(IDEA快捷键.assets/image-20200603100128164.png)
  98. . :匹配任意字符
  99. + :出现1次或多次
  100. */
  101. String regex = "!\\[.*\\]\\(.+\\)";
  102. // 匹配文章中所有的图片标签
  103. Matcher matcher = Pattern.compile(regex).matcher(mdFileContent);
  104. // imageNames 用于存储匹配到的图片标签
  105. List<String> imageNames = new ArrayList<>();
  106. //遍历匹配项,将其添加至集合中
  107. while (matcher.find()) {
  108. // 得到当前图片标签
  109. String curImageLabel = matcher.group();
  110. // 放心大胆地使用"/"截取子串,因为文件名不能包含"/"字符
  111. Integer picNameStartIndex = curImageLabel.lastIndexOf("/") + 1;
  112. Integer picNameEndIndex = curImageLabel.length() - 1;
  113. // 得到图片名称
  114. String curImageName = curImageLabel.substring(picNameStartIndex, picNameEndIndex);
  115. // 添加至集合中
  116. imageNames.add(curImageName);
  117. }
  118. // 转换为数组返回
  119. String[] retStrs = new String[imageNames.size()];
  120. return imageNames.toArray(retStrs);
  121. }
  122. /**
  123. * 读取MD文件的内容
  124. * @param curFile MD文件的File对象
  125. * @return
  126. */
  127. public static String readMdFileContent(File curFile) {
  128. // 存储md文件内容
  129. StringBuilder sb = new StringBuilder();
  130. // 当前行内容
  131. String curLine;
  132. // 装饰者模式:FileReader无法一行一行读取,所以使用BufferedReader装饰FileReader
  133. try (
  134. FileReader fr = new FileReader(curFile);
  135. BufferedReader br = new BufferedReader(fr);
  136. ) {
  137. // 当前行有内容
  138. while ((curLine = br.readLine()) != null) {
  139. sb.append(curLine + "\r\n");
  140. }
  141. } catch (IOException e) {
  142. e.printStackTrace();
  143. }
  144. // 返回md文件内容
  145. return sb.toString();
  146. }
  147. /**
  148. * 清除无用的图片
  149. * @param allPicFiles 所有本地图片的File对象数组
  150. * @param usedPicNames MD文件中使用到的图片名称数组
  151. */
  152. private static void CleanUnusedPic(File[] allPicFiles, String[] usedPicNames) {
  153. // assets文件夹中如果没有图片,则直接返回
  154. if (allPicFiles == null || allPicFiles.length == 0) {
  155. return;
  156. }
  157. // 获取asset文件夹的绝对路径
  158. String assetPath = allPicFiles[0].getParent();
  159. // 为了便于操作,将数组转换为List
  160. List<String> usedPicNameList = Arrays.asList(usedPicNames);
  161. // 遍历所有本地图片,看看有哪些图片没有被使用
  162. for (File curPicFile : allPicFiles) {
  163. // 如果没有被使用,则添加至unusedPicNames集合
  164. String curFileName = curPicFile.getName();
  165. boolean isUsed = usedPicNameList.contains(curFileName);
  166. if (!isUsed) {
  167. // 创建File对象,用于删除
  168. String curPicAbsolutePath = curPicFile.getAbsolutePath();
  169. // 测试用:打印输出
  170. System.out.println("已删除无用图片:" + curPicAbsolutePath);
  171. // 删除文件,看看回收站还有没有,并没有。。。
  172. curPicFile.delete();
  173. }
  174. }
  175. }
  176. }

5、效果

  • 程序控制台输出的日志:

image-20200712193652927

6、警告

  • 谨慎使用,图片一旦删除无法找回,回收站都没得。。。

发表评论

表情:
评论列表 (有 0 条评论,108人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Android - App

    为什么要瘦身 安装包变大,导致很多用户不愿意安装更新 安装包变大,导致很多用户不愿意下载 安装包变大,流量使用增多,增加其他边际成本 优化方式

    相关 springboot 项目

    一、前言 [Spring Boot][Spring_Boot]部署起来虽然简单,如果服务器部署在公司内网,速度还行,但是如果部署在公网,部署起来实在头疼:编译出来的 Jar

    相关 读书,方法试行

    昨天到商场那边等人,商场有一角是卖书的,里面原杂志真的很多啊,有非常不错的,比较"上层"的杂志;平时也知道这些杂志很好看,10块20块一本的很贵,买起来不划算; 在商场里看书

    相关 Android-APP

           为了给用户带来更好的体验,我们应该为用户着想,首先要简化我们的app,去给我们的app瘦身,一次来减少app为用户带来的不良影响。       app瘦身有两种

    相关 spring boot

    Spring Boot 越来越流行,使用Spring Boot 技术的公司和项目也越来越多, 相比之前框架中大量的配置文件,繁琐的配置确实方便了很多,提高了开发的效率. 不同