按照.${filename}.assets
的格式批量整理markdown文件的图片存放路径
由于我的笔记和图都在一个根目录下,且之前的图片存放路径都是在本层级的相对路径,适用于绝对路径和本层级及以下的相对路径;图片只考虑了jpg和png两种情况,有其他需求可以修改strings.HasPrefix这部分代码
// ChangeDir 移动照片(文件都存放在md笔记同目录下) 传入的rootPath格式参照 "D:/AStudy/typora"
func ChangeDir(rootPath string) {
files, err := ioutil.ReadDir(rootPath)
if err != nil {
fmt.Printf("读取目录【%v】失败,错误:%+v\n", rootPath, err)
return
}
//正则匹配文件链接
//<img src=".02-1wagger给前端看的文档.assets\image-2013405.png" alt="image-2065013405" style="zoom:70%;" />
//![算法名称是否唯一](.02-2Q&R.assets\算法名称是否唯一.png)
reg := regexp.MustCompile(`(\<img src=\"|\]\(){1,1}([^\^)"]*)`)
//遍历文件
for _, file := range files {
filename := file.Name()
//是目录
if file.IsDir() {
//递归接着读
ChangeDir(rootPath + "/" + filename)
} else { //是文件
//判断是否为md文件
ext := filepath.Ext(filename)
if ext == ".md" {
filePath := rootPath + "/" + filename
buf, err := ioutil.ReadFile(filePath)
if err != nil {
fmt.Printf("读取文件【%v】失败,错误:%+v\n", filePath, err)
return
}
// 每次更新链接的暂存数据
tmpMdStr := string(buf)
// 匹配图片格式正则
matches := reg.FindAllStringSubmatch(tmpMdStr, -1)
//遍历每一个图片,判断图片是否存在
for _, match := range matches {
//获取全部路径
mdImgPath := match[2]
//是本地图片(以.jpg/.png结尾,不是http开头
if !strings.HasPrefix(mdImgPath, "http") && (strings.HasSuffix(mdImgPath, ".png") || strings.HasSuffix(mdImgPath, ".jpg")) {
//不是绝对路径
if !filepath.IsAbs(mdImgPath) {
//添加前缀,改为绝对路径
mdImgPath = rootPath + "/" + mdImgPath
}
//是否存在该文件
flag, sourcePath := fileExists(mdImgPath)
if flag {
//md文件名(无后缀)
fileStr := filename[:len(filename)-len(ext)]
//应存放的文件夹名称
toPath := "." + fileStr + ".assets"
//应存放的文件夹的绝对路径
fullDir := rootPath + "/" + toPath
//md文件里图片的相对路径
toMatch := toPath + "/" + filepath.Base(sourcePath)
//图片的绝对路径
destPath := rootPath + "/" + toMatch
//判断目的文件夹是否存在,不存在就创建
err = checkDir(fullDir)
if err != nil {
fmt.Printf("创建文件夹【%v】失败,错误:%+v\n", toPath, err)
return
}
//如果目的地址不一样,则移动文件并更新链接
if sourcePath != destPath {
//移动文件
err := os.Rename(sourcePath, destPath)
if err != nil {
fmt.Printf("移动文件【%v】到【%v】失败,错误:%+v\n", sourcePath, destPath, err)
} else {
//更新md文档链接
tmpMdStr = strings.Replace(tmpMdStr, match[2], toMatch, 1)
//重新写入
err := ioutil.WriteFile(filePath, []byte(tmpMdStr), 0)
if err != nil {
fmt.Printf("md更新链接失败:【%v】到【%v】,错误:%+v\n", sourcePath, destPath, err)
//试图恢复图片,如果再失败则无法挽回
err := os.Rename(destPath, sourcePath)
if err != nil {
fmt.Printf("【回滚失败,图片已移动但文档未更新,请尝试失败的操作恢复文档可读性】试图恢复:移动文件【%v】到【%v】失败,错误:%+v\n", destPath, sourcePath, err)
}
}
}
}
} else {
fmt.Printf("%v下文件%v不存在\n", rootPath+"/"+filename, sourcePath)
}
}
}
}
}
}
}
// fileExists 文件是否存在
func fileExists(filePath string) (bool, string) {
_, err := os.Stat(filePath)
flag := err == nil || os.IsExist(err)
//如果不存在
if !flag {
//%还原为中文再判断一次
sourcePath2, _ := url.QueryUnescape(filePath)
_, err := os.Stat(sourcePath2)
flag = err == nil || os.IsExist(err)
if flag {
return true, sourcePath2
}
return false, filePath
}
return true, filePath
}
// 判断并新建文件夹
func checkDir(dirPath string) error {
// 使用Stat来检查目录是否存在
if _, err := os.Stat(dirPath); os.IsNotExist(err) {
// 目录不存在,使用MkdirAll创建
err := os.MkdirAll(dirPath, os.ModePerm)
if err != nil {
return err
}
}
return nil
}
调用ChangeDir()方法,传入笔记的根目录即可一键整理。
func main() {
rootPath := "D:/AStudy/typora"
ChangeDir(rootPath)
}
以下main函数方法会把控制台内容输出到文件中,方便整理失效/移动失败的图片:
// 移动照片
func main() {
// 打开文件
file, err := os.OpenFile("output.txt", os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0666)
if err != nil {
fmt.Println("Error opening file:", err)
return
}
defer file.Close() // 确保文件在结束时关闭
// 保存原始的输出
origStdout := os.Stdout
// 将输出重定向到文件
os.Stdout = file
rootPath := "D:/AStudy/副本/typora - 副本/mudi"
ChangeDir(rootPath)
// 恢复标准输出到原来的位置
os.Stdout = origStdout
// 关闭文件
file.Close()
}