这里写自定义目录标题
MarkDown图片文件打包器
我平时喜欢用MarkDown写笔记,看着效果比word舒服多了,用QQ截图粘贴到MarkDown简直不要太舒服,有一天我把MarkDown文件发给同学,结果里面的图片都不能显示,后来了解到MarkDown文件的图片显示通过地址引用的方式,而Word是直接把图片集成进文件中的。这时候突然觉得MarkDown也不是很香了,但是又实在是受不了Word的那种格式,于是开始每次截一张图我都要把他存在.md文件当前目下photo目录下,但是这么做就觉得很烦,烦就烦了吧,最主要的是你保存到了photo目录下,如果是用QQ截图直接ctrl+c的方式的话,在你MarkDown文件中还是个全路径(绝对路径),你把文件连图片一起发到别的电脑上,还是显示不了,这就很头疼。
于是我去各大网站搜索MarkDown打包器什么的无果后,写了一个MarkDown图片文件打包器,你只需要关注写就可以,等你写完了执行打包器把所以文件中的图片导出到一个文件下,MarkDown文件中也全部使用相对路径,这样你就可以发给任何人都可以正常显示图片信息了。
使用说明
方式一
直接点击选择文件,选择文件后,点击开始打包
方式二
要是懒得用文件选择器找文件,可以直接填写路径,或者直接摁住shift + 右键点击 文件 ==>
选择复制文件地址 ==>粘贴出来的格式大概是 【“路径”】两边会带有双引号,不用担心双引号,里面代码已经把双引号处理过了,你不用特意去处理双引号,我的宗旨是怎么方便怎么来了(怎么懒怎么来)
然后点击开始打包就行
项目下载地址
MarkDown打包器源码
FileUtils.java
package com.sk.packmarkdown.code;
import javax.swing.filechooser.FileSystemView;
import java.io.*;
import java.nio.channels.FileChannel;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
public class FileUtils {
private static String DeskTop = null;
private FileUtils() {
}
/**
* 解析文件为List<String>,文件中的每一行内容 是一个list中的item
* @param filePath 文件的绝对路径
* @return List<String>
*/
public static List<String> readFile(String filePath) {
File file = new File(filePath);
BufferedReader br = null;
try {
//判断是否存在
if (!file.exists()) {
throw new FileNotFoundException();
}
br = new BufferedReader(new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8));
List list = new ArrayList();
String line;
while ((line = br.readLine()) != null) {
list.add(line);
}
return list;
} catch (Exception e) {
e.printStackTrace();
} finally {
if (br != null) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return null;
}
/**
* 输出文件
* @param targetFilePath 输出文件的全路径(D:\??\??\??\??.??)
* @param data data就是写入的信息 data中每一个item是写入后的一行
*/
public static void writeFile(String targetFilePath, List<String> data) {
BufferedWriter bw = null;
try {
bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(targetFilePath), StandardCharsets.UTF_8));
for (String datum : data) {
bw.write(datum + "\n");
}
bw.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (bw != null) {
try {
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 文件复制
* @param filePath 源文件路径
* @param targetFilePath 输出路径
*/
public static void copyFile(String filePath, String targetFilePath) {
if (!new File(filePath).exists()) {
return;
}
FileChannel inputChannel = null;
FileChannel outputChannel = null;
try {
inputChannel = new FileInputStream(filePath).getChannel();
outputChannel = new FileOutputStream(targetFilePath).getChannel();
outputChannel.transferFrom(inputChannel, 0, inputChannel.size());
} catch (Exception e) {
} finally {
try {
inputChannel.close();
outputChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* (批量)相对路径转绝对路径
*
* @param basePath markdown所在的文件夹地址
* @param paths 图片路径集合
* @return
*/
public static List<String> toAbsolutePaths(String basePath, List<String> paths) {
List<String> result = new ArrayList<>();
paths.forEach(item -> {
result.add(toAbsolutePath(basePath, item));
});
return result;
}
/**
* 相对路径转为绝对路径
*
* @param basePath markdown所在的文件夹地址
* @param pathInfo 图片路径信息
* @return
*/
public static String toAbsolutePath(String basePath, String pathInfo) {
File file = new File(pathInfo);
//第一种情况 如果已经是绝对路径了那么一定存在所以直接返回
if (file.exists()) {
return file.getAbsolutePath();
}
File base = new File(basePath);
//全部替换成\反斜杠.然后进行判断
String str = pathInfo.replaceAll("/", "\\\\");
if (str.startsWith("..\\") || str.startsWith(".\\")) {
File file1 = new File(base + "\\" + pathInfo);
String canonicalPath = "";
try {
canonicalPath = file1.getCanonicalPath();
} catch (IOException e) {
e.printStackTrace();
}
return canonicalPath;
} else {
//特殊情况没有【.】或者【\】的出现
File common = new File(basePath + "\\" + pathInfo);
if (common.exists()) {
return common.getAbsolutePath();
}
// System.err.println("出现不合法路径 -> " + pathInfo);
return pathInfo;
}
}
//获取用户电脑桌面地址(Windows)
public static String getDeskTop() {
if (DeskTop == null) {
FileSystemView fileSystemView = FileSystemView.getFileSystemView();
File homeDirectory = fileSystemView.getHomeDirectory();
DeskTop = homeDirectory.toString();
}
return DeskTop;
}
}
StringUtils.java
package com.sk.packmarkdown.code;
public class StringUtils {
//判断空
public static boolean isBlack(String str) {
return str == null || str.equals("");
}
//去除字符串中的所有空格
public static String trimAll(String str) {
return str.replaceAll("\\s", "");
}
}
MarkDownPhoto.java
package com.sk.packmarkdown.code;
/**
* MarkDownPhoto用于存储MarkDown中图片信息以及转化后的信息
*/
public class MarkDownPhoto {
//正则过滤出来值 用于替换时当做条件使用
//格式如下
private String regexPhotoPath;
//图片文件存在的绝对路径
//格式如下D:\????\????\1665320692833.png
private String absolutePhotoPath;
//导出后图片所在的绝对路径 供图片导出(copy)使用
//格式如下E:\????\photo\1665320692833.png
private String targetAbsolutePhotoPath;
//导出后文件中MarkDown图片引用应该填入的信息
//格式如下.\photo\1665320692833.png
private String targetFilePhotoInfo;
public MarkDownPhoto() {
}
public MarkDownPhoto(String regexPhotoPath, String absolutePhotoPath, String targetAbsolutePhotoPath, String targetFilePhotoInfo) {
this.regexPhotoPath = regexPhotoPath;
this.absolutePhotoPath = absolutePhotoPath;
this.targetAbsolutePhotoPath = targetAbsolutePhotoPath;
this.targetFilePhotoInfo = targetFilePhotoInfo;
}
public String getRegexPhotoPath() {
return regexPhotoPath;
}
public void setRegexPhotoPath(String regexPhotoPath) {
this.regexPhotoPath = regexPhotoPath;
}
public String getAbsolutePhotoPath() {
return absolutePhotoPath;
}
public void setAbsolutePhotoPath(String absolutePhotoPath) {
this.absolutePhotoPath = absolutePhotoPath;
}
public String getTargetAbsolutePhotoPath() {
return targetAbsolutePhotoPath;
}
public void setTargetAbsolutePhotoPath(String targetAbsolutePhotoPath) {
this.targetAbsolutePhotoPath = targetAbsolutePhotoPath;
}
public String getTargetFilePhotoInfo() {
return targetFilePhotoInfo;
}
public void setTargetFilePhotoInfo(String targetFilePhotoInfo) {
this.targetFilePhotoInfo = targetFilePhotoInfo;
}
@Override
public String toString() {
return "MarkDownPhoto{" +
"regexPhotoPath='" + regexPhotoPath + '\'' +
", absolutePhotoPath='" + absolutePhotoPath + '\'' +
", targetAbsolutePhotoPath='" + targetAbsolutePhotoPath + '\'' +
", targetFilePhotoInfo='" + targetFilePhotoInfo + '\'' +
'}';
}
}
MarkDownUtils.java
package com.sk.packmarkdown.code;
import java.io.File;
import java.io.FileNotFoundException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class MarkDownUtils {
private String filePath;
private String fileName;
private String targetFilePath;
private static final String PHOTO_FILTER_RULE = "!\\[[^\\]]+\\]\\([^\\)]+\\).*";
/**
*
* @param filePath MarkDown文件所在文件夹路径
* @param fileName MarkDown文件的名字 例如 abc.md
*/
public MarkDownUtils(String filePath, String fileName) {
this.filePath = filePath;
this.fileName = fileName;
//默认统一导出地址为用户所在的桌面下 以MarkDown文件名字作为文件夹 例如:fileName="abc.md" 那么输出地址就是 略过\桌面\abc
this.targetFilePath = FileUtils.getDeskTop() + "\\" + fileName.substring(0, fileName.indexOf("."));
//如果abc文件夹存在 将以abc+当前时间字符串 作为桌面唯一的文件夹
if (new File(targetFilePath).exists()) {
Date date = new Date();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy年MM月dd日HH时mm分ss秒SSS毫秒");
String format = simpleDateFormat.format(date);
targetFilePath = FileUtils.getDeskTop() + "\\" + fileName.substring(0, fileName.indexOf(".")) + format;
}
}
/**
* 解析MarkDown文件为List<String>
* @return List<String>
*/
public List<String> getFileContent() {
File file = new File(filePath + "\\" + fileName);
if (file.exists()) {
return FileUtils.readFile(file.getAbsolutePath());
} else {
try {
throw new FileNotFoundException();
} catch (FileNotFoundException e) {
e.printStackTrace();
}
return null;
}
}
/**
* 批量获取MarkDownPhoto对象
* @param fileContent 经过FileUtils解析文件的值
* @return List<MarkDownPhoto>
*/
private List<MarkDownPhoto> getMarkDownPhotos(List<String> fileContent) {
List<MarkDownPhoto> regexPhotoPath = new ArrayList<>();
fileContent.forEach(a -> {
//通过正则找到markdown中的图片信息
if (StringUtils.trimAll(a).matches(PHOTO_FILTER_RULE)) {
regexPhotoPath.add(getMarkDownPhoto(a));
}
});
return regexPhotoPath;
}
/**
* 获取MarkDownPhoto对象 传入通过正则过滤后的图片信息 例如:
* @param filePhotoPath
* @return
*/
private MarkDownPhoto getMarkDownPhoto(String filePhotoPath) {
MarkDownPhoto markDownPhoto = new MarkDownPhoto();
//设置进去的值为 【】
markDownPhoto.setRegexPhotoPath(filePhotoPath);
String photoPath = filePhotoPath.substring(filePhotoPath.indexOf("(") + 1, filePhotoPath.indexOf(")"));
//setAbsolutePhotoPath的值为【D:\???\????\???\photo\image-20230127142151213.png】
String absolutePath = FileUtils.toAbsolutePath(this.filePath, photoPath);
markDownPhoto.setAbsolutePhotoPath(absolutePath);
File file = new File(absolutePath);
//设置进去的是输出目录的图片地址
markDownPhoto.setTargetAbsolutePhotoPath(this.targetFilePath + "\\" + "photo" + "\\" + file.getName());
//设置进去的是输出MarkDown文件中的图片引用路径统一使用.\相对路径格式
markDownPhoto.setTargetFilePhotoInfo(".\\photo\\" + file.getName());
return markDownPhoto;
}
/**
* 执行流程
* 1.解析文件为List data
* 2.通过data来匹配正则,解析成MarkDownPhoto对象
* 3.通过List<MarkDownPhoto>的每个item.regexPhotoPath匹配data中的信息如果匹配到了就把原文中的信息替换成item.targetFilePhotoInfo信息
* 同时再调用copy(item.absolutePhotoPath,item.targetAbsolutePhotoPath)
* 最后在写出文件
*
*/
public void execute() {
File file = new File(targetFilePath + "\\" + "photo");
file.mkdirs();
List<String> fileContent = getFileContent();
List<MarkDownPhoto> markDownPhotos = getMarkDownPhotos(fileContent);
//markDownPhotos 的索引
int index = 0;
for (int i = 0; i < fileContent.size(); i++) {
MarkDownPhoto markDownPhoto = markDownPhotos.get(index);
if (StringUtils.trimAll(fileContent.get(i)).equals(StringUtils.trimAll(markDownPhoto.getRegexPhotoPath()))) {
//获取原文内容
String line = fileContent.get(i);
String newLine = line.substring(0, line.indexOf("(") + 1) + markDownPhoto.getTargetFilePhotoInfo() + line.substring(line.indexOf(")"));
//替换原文中的值
fileContent.set(i, newLine);
//复制图片文件到指定导出目录
FileUtils.copyFile(markDownPhoto.getAbsolutePhotoPath(), markDownPhoto.getTargetAbsolutePhotoPath());
//索引加一
index++;
//提高效率 因为markDownPhotos的索引都到最后一位了,那么代表源文件下面几行不会存在图片文件了所以可以直接姐退出了
if (index == markDownPhotos.size()) {
break;
}
}
}
//写出文件
FileUtils.writeFile(targetFilePath + "\\" + fileName, fileContent);
}
}
MainFrame(Java Swing窗口操作类)
package com.sk.packmarkdown.frame;
import com.sk.packmarkdown.code.MarkDownUtils;
import com.sk.packmarkdown.code.StringUtils;
import javax.swing.*;
import javax.swing.filechooser.FileFilter;
import java.awt.*;
import java.io.File;
public class MainFrame extends JFrame {
private Integer screenWidth;
private Integer screenHeight;
private Integer cWidth = 650;
private Integer cHeight = 500;
private Container c;
/**
* 获取Windows显示器的宽高分辨率
*/ {
Toolkit toolkit = Toolkit.getDefaultToolkit();
Dimension dimension = toolkit.getScreenSize();
screenWidth = dimension.width;
screenHeight = dimension.height;
}
MainFrame() {
initScreen();
int width = 250;
int height = 50;
JLabel jl = new JLabel("MarkDown打包器");
jl.setFont(new Font("楷体", Font.BOLD, 30));
jl.setBounds(center(cWidth, width), center(cHeight, height) - 200, width, height);
c.add(jl);
//文本框的标签
int jlWidth = 105;
int jlHeight = 50;
JLabel jLabel = new JLabel("文件路径:");
jLabel.setBounds(center(this.cWidth, jlWidth) - 250, center(this.cHeight, jlHeight), jlWidth, jlHeight);
jLabel.setFont(new Font("楷体", Font.BOLD, 20));
c.add(jLabel);
//文本框
int jtfWidth = 450;
int jtfHeight = 50;
JTextField jTextField = new JTextField();
jTextField.setBounds(center(this.cWidth, jtfWidth) + 25, center(this.cHeight, jtfHeight), jtfWidth, jtfHeight);
jTextField.setFont(new Font("楷体", Font.BOLD, 20));
c.add(jTextField);
int btnWidth = 130;
int btnHeight = 50;
//选择文件按钮
JButton fileChoose = new JButton("选择文件");
fileChoose.setBounds(center(this.cWidth, btnWidth), center(this.cHeight, btnHeight) - 75, btnWidth, btnHeight);
fileChoose.setFont(new Font("楷体", Font.BOLD, 20));
fileChoose.addActionListener(e -> {
System.out.println(getX());
System.out.println(getY());
JFileChooser jfc = new JFileChooser();
jfc.setFileFilter(new FileFilter() {
@Override
public boolean accept(File f) {
return f.getName().endsWith("md");
}
@Override
public String getDescription() {
return ".md";
}
});
jfc.showOpenDialog(this);
if (jfc.getSelectedFile() == null) {
return;
}
String absolutePath = jfc.getSelectedFile().getAbsolutePath();
jTextField.setText(absolutePath);
});
c.add(fileChoose);
//打包按钮
JButton jButton = new JButton("开始打包");
jButton.setBounds(center(this.cWidth, btnWidth), center(this.cHeight, btnHeight) + 75, btnWidth, btnHeight);
jButton.setFont(new Font("楷体", Font.BOLD, 20));
jButton.addActionListener(e -> {
String text = jTextField.getText();
if (StringUtils.isBlack(StringUtils.trimAll(text))) {
JDialog dialog = new JDialog(this);
dialog.setTitle("warning");
dialog.setBounds(center(this.screenWidth, 300), center(this.screenHeight, 100), 300, 100);
JLabel content = new JLabel("请选择文件");
dialog.add(content);
dialog.show();
return;
}
if (text.startsWith("\"")) {
text = text.substring(1);
}
if (text.endsWith("\"")) {
text = text.substring(0, text.length() - 1);
}
File file = new File(text);
if (!file.exists() || !file.getName().endsWith("md")) {
JDialog dialog = new JDialog(this);
dialog.setTitle("warning");
dialog.setBounds(center(this.screenWidth, 300), center(this.screenHeight, 100), 300, 100);
JLabel content = new JLabel("文件不存在");
if (!file.getName().endsWith("md")) {
content.setText("不是MarkDown文件");
}
dialog.add(content);
dialog.show();
return;
}
MarkDownUtils markDownUtils = new MarkDownUtils(file.getParent(), file.getName());
markDownUtils.execute();
jTextField.setText("");
JDialog dialog = new JDialog(this);
dialog.setTitle("warning");
dialog.setBounds(center(this.screenWidth, 300), center(this.screenHeight, 100), 300, 100);
JLabel content = new JLabel("打包完成");
dialog.add(content);
dialog.show();
System.gc();
});
c.add(jButton);
}
/**
* 初始化窗口
*/
void initScreen() {
c = getContentPane();
//设置容器使用自定义布局
c.setLayout(null);
//设置窗口位置及大小
setBounds(center(screenWidth, cWidth), center(screenHeight, cHeight), cWidth, cHeight);
//设置窗口关闭规则
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
//设置窗口不可变
setResizable(false);
}
/**
* 计算居中数值(可以计算宽高)
*
* @param container 父容器宽\高值
* @param subContainer 子容器宽\高值
* @return
*/
private int center(int container, int subContainer) {
return (container - subContainer) / 2;
}
public static void main(String[] args) {
try {
new MainFrame().setVisible(true);
} catch (Exception e) {
}
}
}