起因
使用markdown记录学习笔记,发现markdown删除掉不使用的图片后,只是删除了md文件中对图片的引用,图片依旧保留在文件夹中。
在频繁修改md文件时,不使用的图片会越来越多,占用磁盘空间,所以想把不再使用的图片全部删除。
已经有网友实现了这个功能,我为了锻炼下自己的golang的代码能力,所以就自己又实现了一遍。
实现流程
推荐一个markdown编辑软件,目前依旧开源免费。markText
首先,通过配置文件提供md文件和img文件存储的目录。配置文件名叫”config.json“
{
"mddir":"your markdown file directory",
"imgdir":"your img file directory"
}
img目录存储md文件中用到的图片。
实现过程如下:
- 读取配置文件,获取md和img的存储目录
- 遍历md目录,找到所有的markdown文件
- 对于每一个md文件
- 找到文件中的”img.*png“的字符串,认为是图片文件名,并记录到mapUsedPng
- 所有md文件处理完
- 遍历img目录,记录所有的png图片到mapAllpng中
- 将正在使用的png从mapAllpng删除,就得到了md文件中未使用的图片
- 将img中未使用的图片删除
代码实现
我最熟悉c语言,golang是我最近学习的,所以代码中的C语言的气息会比较浓重,将就着看吧。
package main
import (
"bufio"
"encoding/json"
"fmt"
"io"
"log"
"os"
"regexp"
"strings"
)
type cofigFile struct {
MDdir string `json:"mddir"`
ImgDir string `json:"imgdir"`
}
var configForMd cofigFile
var mapPngUsedString map[string]int = make(map[string]int)
var mapPngAll map[string]int = make(map[string]int)
var mdfileList = make([]string, 0)
func main() {
//获取Markdown文件和img文件存储的路径
getConfigFile("./config.json")
//遍历markdown文件夹,找到所有md文件
scanMDdir(configForMd.MDdir)
fmt.Println("len(mdfileList):", len(mdfileList))
for index := 0; index < len(mdfileList); index++ {
mdfileName := mdfileList[index]
fmt.Println("scan mdfile[", index, "]:", configForMd.MDdir+mdfileName)
inputFile, inputError := os.Open(configForMd.MDdir + mdfileName)
if inputError != nil {
fmt.Printf("An error occurred on opening the inputfile\n" +
"Does the file exist?\n" +
"Have you got acces to it?\n")
return // exit the function on error
}
defer inputFile.Close()
inputReader := bufio.NewReader(inputFile)
for {
inputString, readerError := inputReader.ReadString('\n')
//fmt.Printf("The input was: %s", inputString)
if readerError == io.EOF {
break
}
// 使用正则表达式,寻找png的路径和文件名,
matchStr, _ := regFindImg(inputString)
// 将寻找到的在使用的png的文件名记录到map中
storePngUsedfile(matchStr)
}
}
for k, _ := range mapPngUsedString {
fmt.Println("used:", k)
}
// 将img目录下的png文件名记录到mapPngAll中
getPngALL(configForMd.ImgDir)
for k, _ := range mapPngAll {
fmt.Println("all png:", k)
}
// 过滤掉没有使用的png图片文件名
filterUnusedPngFile()
// 删除掉未使用的png图片
deleteUnusedPng(configForMd.ImgDir)
//等待用户输入,以便看到过程中的打印
var temp int
fmt.Scanln(&temp)
}
// 遍历markdown文件夹下,找到所有Markdown文件
func scanMDdir(mddirPath string) {
fileinfo, err := os.Open(mddirPath)
if err != nil {
return
}
files, err := fileinfo.Readdir(-1)
fileinfo.Close()
if err != nil {
return
}
for _, file := range files {
//fmt.Println("mddir:", file.Name())
if !file.IsDir() && regFindmd(file.Name()) {
mdfileList = append(mdfileList, file.Name())
fmt.Println("mdfile:", file.Name())
}
}
}
func regFindmd(filename string) bool {
if strings.HasSuffix(filename, ".md") {
return true
} else {
return false
}
}
// 删除掉未使用的png图片
func deleteUnusedPng(imgdir string) {
for filename, _ := range mapPngAll {
delfinename := imgdir + filename
fmt.Println("del file:", delfinename)
os.Remove(delfinename)
}
}
// 过滤掉没有使用的png图片文件名
func filterUnusedPngFile() {
for allpng, _ := range mapPngAll {
if _, ok := mapPngUsedString[allpng]; ok {
fmt.Println(allpng, "is used")
delete(mapPngAll, allpng)
}
}
}
// 将img目录下的png文件名记录到mapPngAll中
func getPngALL(dirpath string) {
files, err := os.Open(dirpath) //open the directory to read files in the directory
if err != nil {
fmt.Println("error opening directory:", err) //print error if directory is not opened
return
}
defer files.Close() //close the directory opened
fileInfos, err := files.Readdir(-1) //read the files from the directory
if err != nil {
fmt.Println("error reading directory:", err) //if directory is not read properly print error message
return
}
for _, fileInfos := range fileInfos {
//fmt.Println("img dir:", fileInfos.Name()) //print the files from directory
mapPngAll[fileInfos.Name()] = 1
}
}
// 获取Markdown文件和img文件存储的路径
func getConfigFile(filepath string) {
file, err := os.ReadFile(filepath)
if err != nil {
log.Fatal(err)
}
err = json.Unmarshal(file, &configForMd)
if err != nil {
log.Fatal(err)
}
fmt.Println("mddir:", configForMd.MDdir)
fmt.Println("pngdir:", configForMd.ImgDir)
configForMd.MDdir += "\\"
configForMd.ImgDir += "\\"
}
// 将寻找到的在使用的png的文件名记录到map中
func storePngUsedfile(matchStr []string) {
strlength := len(matchStr)
for i := 0; i < strlength; i++ {
//寻找到的字符串是”img/.*png“,比如”img/test.png“,
//实际需要存储的文件名是”test.png“,
//所以实际存储的字符串要去除前四个字符,实际存储是matchStr[i][4:]
mapPngUsedString[matchStr[i][4:]] = 1
}
}
// 使用正则表达式,寻找png的路径和文件名,并通过match数组返回
func regFindImg(content string) (match []string, err error) {
re := regexp.MustCompile("img/.*png")
found := re.FindAllString(content, -1)
if found == nil {
//fmt.Printf("no match found\n")
return nil, err
}
//fmt.Printf("------found: %q\n", found)
return found, nil
}
注意事项
1、推荐的md和img的存储结构如下,
|–任意一个目录,假设为A
~~~
|—001.md
~~~
|—002.md
~~~
****
~~~
|—xxx.md
~~~
|—img
~~~~~~
|— used001.png
~~~~~~
|— used002.png
最好将md的图片存储路径放到当前目录的img目录,
如果img存储了其他路径B dir的md文件的图片,就无法知道B路径使用了哪些图片,就会误删除B路径的md文件存储在A路径的img图片。
2、Marktext设置md的图片存储路径的设置如下: