所有演示均在typora环境下,其他markdown软件不清楚是否都能用,使用之前建议备份一遍,防止出现问题。
前置操作
打开typora
,进入文件
->偏好设置
->图像
按照下面的图片勾选被填写图片相对路径(方便以后使用)相对路径:
./pictures/${filename}.assets
Java代码
Main.java
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Scanner;
public class Main {
public static void main(String[] args) throws IOException {
Scanner sc=new Scanner(System.in);
System.out.println("请输入笔记文件目录路径:");
String srcNotePath,outNotepath;
while(true){
srcNotePath= sc.next();
File temp=new File(srcNotePath);
if(!temp.exists())
System.out.println("该目录不存在");
else
break;
}
System.out.println("请输入笔记文件输出目录: ");
while(true){
outNotepath = sc.next();
File temp=new File(outNotepath);
if(!temp.exists())
System.out.println("该目录不存在");
else
break;
}
FileProcess fileProcess = new FileProcess(srcNotePath,outNotepath);
SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
File file=new File(outNotepath+File.separator+simpleDateFormat.format(new Date())+".log");
if(file.exists()){
FileWriter fw=new FileWriter(file);
fw.write("");
fw.flush();
}else {
file.createNewFile();
}
PrintStream out=System.out;
System.setOut(new PrintStream(new FileOutputStream(file)));
long start = System.currentTimeMillis();
fileProcess.start();
long end = System.currentTimeMillis();
System.setOut(out);
System.out.println("文件处理完成,耗费时间:"+String.format("%.2f",(end-start)/1000.0)+"s,详细日志地址:"+file.getAbsolutePath());
}
}
FileProcess.java
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class FileProcess {
private String text;
private final String srcNoteDir;
private final String outNoteDir;
private final static String outImgDir ="pictures/${fileName}.assets";
public FileProcess(String srcNoteDir, String outNoteDir) {
this.srcNoteDir = srcNoteDir;
this.outNoteDir = outNoteDir;
}
//启动器
public void start(){
//存储md文件绝对路径
Set<String> filesPathSet=new HashSet<>();
File file = new File(srcNoteDir);
//找到该目录下的所有md文件
if(file.isDirectory()){
File[] files=file.listFiles();
if(files==null){
System.out.println("目录["+srcNoteDir+"]下没有文件");
return;
}
for (File file1 : files) {
if(file1.isDirectory())
continue;
String name=file1.getName().trim().toLowerCase();
if(name.lastIndexOf(".md")==name.length()-3){
filesPathSet.add(file1.getAbsolutePath());
}
}
}else {
System.out.println("路径"+srcNoteDir+"只能是目录 ");
return;
}
filesPathSet.forEach((value)->{
//value:md文件绝对路径
System.out.println("----------------------------------------------------------------------------");
System.out.println("开始对["+value+"]文件进行处理");
//从该文件中读取出文本
text = this.read(value);
if(text==null) return;
Map<String, String> imgMap = process(value);
String folderName=new File(value).getName();
int end = folderName.toLowerCase().lastIndexOf(".md");
//获取md文件的文件名,去掉后缀
folderName=folderName.substring(0,end).trim();
//获取该md文件中图片最终存储的路径
String imgOutPath=outNoteDir +File.separator+ outImgDir.replace("${fileName}",folderName);
//复制图片
if(writeImg(imgMap,imgOutPath,new File(value).getParent())){
//将修改后的md文件内容写入到新的文件
writeMd(new File(value).getName());
}
System.out.println("结束对["+value+"]文件的处理");
System.out.println("----------------------------------------------------------------------------");
});
}
//从笔记路径[srcNotePath]读取出文本
private String read(String srcNotePath){
File file=new File(srcNotePath);
StringBuilder text=new StringBuilder();
try(FileInputStream fis=new FileInputStream(file);
BufferedReader reader=new BufferedReader(new InputStreamReader(fis))){
while(reader.ready())
text.append((char)reader.read());
}catch (IOException e){
System.out.println("文件["+srcNotePath+"]读取失败");
return null;
}
return text.toString();
}
//对不同语法显示的图片进行处理,并返回图片原路径与对应的图片相对路径的哈希表,Map<String,String> key:原图片的路径地址(绝对或相对) value:目标图片相对路径
private Map<String,String> process(String srcNotePath){
final Map<String,String> imgMap=new HashMap<>();
handleTypeOne(imgMap,srcNotePath);
handleTypeTwo(imgMap,srcNotePath);
handleTypeThree(imgMap,srcNotePath);
return imgMap;
}
//匹配: ![]() 语法的图片
private void handleTypeOne(Map<String,String> imgMap,String srcNotePath){
String reg="!\\[.*?]\\(.*?\\)";
Pattern pattern=Pattern.compile(reg);
Matcher matcher=pattern.matcher(text);
File file=new File(srcNotePath);
String fileName=file.getName();
int end=fileName.lastIndexOf(".md");
fileName=fileName.substring(0,end);
while(matcher.find()){
String srcPath = matcher.group();
int start = srcPath.indexOf("(");
int lastIndexOf = srcPath.lastIndexOf(")");
srcPath=srcPath.substring(start+1,lastIndexOf).trim();
if(imgMap.containsKey(srcPath)) continue;
if("".equals(srcPath))
continue;
File temp=new File(srcPath);
if(!temp.isAbsolute()){
temp = new File(file.getParent()+File.separator+srcPath);
}
if(!temp.exists()||temp.isDirectory()){
System.out.println("图片["+srcPath+"]不存在");
continue;
}
String targetPath= outImgDir.replace("${fileName}",fileName)+File.separator+temp.getName();
imgMap.put(srcPath,targetPath);
}
}
//匹配 []: 语法
private void handleTypeTwo(Map<String,String> imgMap,String srcNotePath){
String reg="\\[.*?]:.*?\\s";
Pattern pattern=Pattern.compile(reg);
Matcher matcher=pattern.matcher(text);
File file=new File(srcNotePath);
String fileName=file.getName();
int end=fileName.lastIndexOf(".md");
fileName=fileName.substring(0,end);
while(matcher.find()){
String srcPath = matcher.group();
int start = srcPath.indexOf(":");
srcPath=srcPath.substring(start+1).trim();
Pattern p=Pattern.compile("\\s");
Matcher m=p.matcher(srcPath);
if(m.find())
srcPath=srcPath.substring(0,m.start()).trim();
if(imgMap.containsKey(srcPath))
continue;
File temp=new File(srcPath);
if(!temp.isAbsolute()){
temp = new File(file.getParent()+File.separator+srcPath);
}
if(!temp.exists()||temp.isDirectory()){
System.out.println("图片["+srcPath+"]不存在");
continue;
}
String targetPath= outImgDir.replace("${fileName}",fileName)+File.separator+temp.getName();
imgMap.put(srcPath,targetPath);
}
}
//匹配img标签图片
private void handleTypeThree(Map<String,String> imgMap,String srcNotePath){
String reg="<img.*?src=[\"|']?(.*?)[\"|']\\s*.*?>";
Pattern pattern=Pattern.compile(reg);
Matcher matcher=pattern.matcher(text);
File file=new File(srcNotePath);
String fileName=file.getName();
int end=fileName.lastIndexOf(".md");
fileName=fileName.substring(0,end);
while(matcher.find()){
String srcPath = matcher.group();
Pattern p=Pattern.compile("src=[\"|']?(.*?)[\"|']");
Matcher m=p.matcher(srcPath);
if(m.find())srcPath=srcPath.substring(m.start(),m.end()+1);
int start=srcPath.indexOf("'");
if(start<0) start=srcPath.indexOf("\"");
end=Math.max(srcPath.lastIndexOf("'"),srcPath.lastIndexOf("\""));
srcPath=srcPath.substring(start+1,end);
if(imgMap.containsKey(srcPath))
continue;
File temp=new File(srcPath);
if(!temp.isAbsolute()){
temp = new File(file.getParent()+File.separator+srcPath);
}
if(!temp.exists()||temp.isDirectory()){
System.out.println("图片["+srcPath+"]不存在");
continue;
}
String targetPath= outImgDir.replace("${fileName}",fileName)+File.separator+temp.getName();
imgMap.put(srcPath,targetPath);
}
}
//将当前笔记文本中的srcPath路径字符串替换成相对路径targetPath
private void replace(String srcPath,String targetPath){
srcPath=srcPath.replaceAll("\\\\","\\\\\\\\");
targetPath=targetPath.replaceAll("\\\\","/");
text=text.replaceAll(srcPath,targetPath);
System.out.println(srcPath+"替换成"+targetPath.replaceAll("\\\\","/"));
}
//递归清空文件夹及删除文件夹本身
private void clearFiles(File file){
if(!file.exists()) return;
File[] list = file.listFiles(); //无法做到list多层文件夹数据
if (list != null) {
for (File temp : list) { //先去递归删除子文件夹及子文件
clearFiles(temp); //注意这里是递归调用
}
}
file.delete();
}
//批量在新目录下创建图片,key:原图相对地址或绝对地址,value目标图片相对路径,currImgOutDir:当前md文件中图片的最终存储路径,parentPath:当前md文件的父级路径
private boolean writeImg(Map<String,String> imgMap,String currImgOutDir,String parentPath){
File file=new File(currImgOutDir);
clearFiles(file);
try {
Files.createDirectories(Paths.get(currImgOutDir));
} catch (IOException e) {
System.out.println("文件夹["+currImgOutDir+"]创建失败");
return false;
}
for(Map.Entry<String,String> entry:imgMap.entrySet()){
File srcImg=new File(entry.getKey());
if(!srcImg.isAbsolute()) srcImg=new File(parentPath+File.separator+entry.getKey());
File targetImg =new File(currImgOutDir +File.separator+srcImg.getName());
if(!targetImg.exists()) {
try {
targetImg.createNewFile();
replace(entry.getKey(),entry.getValue());
} catch (IOException e) {
System.out.println("创建图片失败,可能是文件权限不足");
}
}else{
//图片重名时加number后缀
int count=0;
String prefixName="",suffixName="";
int index=srcImg.getName().lastIndexOf(".");
if(index<0) index=srcImg.getName().length();
prefixName=srcImg.getName().substring(0,index);
suffixName=srcImg.getName().substring(index);
do{
++count;
targetImg=new File(currImgOutDir+File.separator+prefixName+"("+count+")"+suffixName);
}while(targetImg.exists());
replace(entry.getKey(),entry.getValue().replace(srcImg.getName(),prefixName+"("+count+")"+suffixName));
}
copyImg(srcImg,targetImg);
}
return true;
}
//重新写入新的md文档
private void writeMd(String currentMdName){
File file=new File(outNoteDir+File.separator+currentMdName);
try(FileWriter fileWriter=new FileWriter(file)){
fileWriter.write("");
fileWriter.flush();
fileWriter.write(text);
System.out.println("文件["+file.getAbsolutePath()+"]写入成功");
} catch (IOException e) {
System.out.println("文件["+file.getAbsolutePath()+"]写入失败");
}
}
//将图片srcImg复制到targetImg
private void copyImg(File srcImg,File targetImg){
try (InputStream is=new FileInputStream(srcImg);
OutputStream os = new FileOutputStream(targetImg)){
byte[] buffer = new byte[1024];
int length;
while ((length = is.read(buffer)) > 0) {
os.write(buffer, 0, length);
}
System.out.println("图片["+srcImg.getAbsolutePath()+"]成功复制到["+targetImg.getAbsolutePath()+"]");
} catch (IOException e) {
System.out.println("图片["+srcImg.getAbsolutePath()+"]移动失败");
}
}
}
使用步骤:直接运行Main.java中的main方法,先输入笔记文件所在的目录,后输入笔记的输出目录(没有创建的先创建),然后程序会开始执行,该程序不修改原笔记内容和图片,只是将修改的内容重新写入到新的文件里面,图片复制到指定目录,原图片不会删除
注意事项:
- 网络图片(URL)不做处理
- 只处理笔记目录下的一层子文件(笔记),超过一层不会处理
- 输出目录建议不要是原笔记目录
- 原笔记建议备份一遍
- 程序执行完毕之后,在输出目录里面有个log文件,为程序执行的日志,会说明程序期间进行了哪些操作,可以删除