Java:Tomcat 9 和 mermaid.min.js 10.9 上传.csv文件实现 Markdown 中 Mermaid 图表的渲染

承上一篇:Java:Tomcat 9, flexmark 0.64 和 mermaid.min.js 10.9 实现 Markdown 中 Mermaid 图表的渲染

我发现 flexmark-all.0.64.8.jar 内有版本冲突问题,现决定去除对Flexmark 0.64.8 的依赖,上传时限制*.csv 和*.txt文件才能上传,保留 CsvToMermaidUtil.java 功能,直接转换为<div class="mermaid">代码</div>·;如何编程?

现在希望彻底移除 Flexmark 依赖,仅保留 CSV/TXT 文件上传功能,且只允许上传 .csv.txt 格式文件,直接将文件内容转换为 <div class="mermaid"> 标签的 HTML 内容,无需再解析 Markdown。我会基于这个需求重新编写完整的可运行代码,核心思路是:去掉所有 Flexmark 相关代码,仅保留文件上传、格式校验、CSV/TXT 转 Mermaid 逻辑,直接生成包含 Mermaid 标签的 HTML 页面。

一、完整实现方案(移除 Flexmark,仅保留 CSV/TXT → Mermaid 转换)

1. 修改 Maven 依赖(pom.xml)

彻底移除 Flexmark 所有依赖,仅保留 Servlet 和文件上传核心依赖:

xml 复制代码
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.mermaid</groupId>
    <artifactId>MdWeb</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>
    <name>CSV/TXT to Mermaid Render WebApp</name>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <servlet-api.version>4.0.1</servlet-api.version>
        <commons-fileupload.version>1.4</commons-fileupload.version>
    </properties>

    <dependencies>
        <!-- Servlet API(Tomcat 9兼容) -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>${servlet-api.version}</version>
            <scope>provided</scope>
        </dependency>

        <!-- 文件上传依赖 -->
        <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>${commons-fileupload.version}</version>
        </dependency>

        <!-- 新增:commons-io 依赖(解决 JDK 8 读取流的问题) -->
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.15.1</version>
        </dependency>
    
        <!-- 日志(可选,便于调试) -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.36</version>
        </dependency>
    </dependencies>

    <build>
        <finalName>MdWeb</finalName>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>3.3.2</version>
                <configuration>
                    <failOnMissingWebXml>false</failOnMissingWebXml>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>
2. 保留并微调 CsvToMermaidUtil.java(直接返回 Mermaid 代码,无需 Markdown 包装)

修改转换逻辑,直接返回 <div class="mermaid"> 包裹的 Mermaid 代码,而非 Markdown 代码块:

java 复制代码
package com.mermaid.util;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class CsvToMermaidUtil {
    /**
     * 校验并转换 CSV/TXT(CSV格式)为 <div class="mermaid"> 包裹的 HTML 内容
     * @param fileName 文件名
     * @param content 文件内容
     * @return 转换结果(valid:是否成功,content:转换后HTML/错误信息)
     */
    public static Result validateAndConvert(String fileName, String content) {
        // 统一换行符,处理不同系统的换行格式
        String normalizedContent = content.replace("\r\n", "\n").replace("\r", "\n");
        // 拆分并过滤空行
        String[] linesArr = normalizedContent.split("\n");
        List<String> lines = new ArrayList<>();
        for (String line : linesArr) {
            if (!line.trim().isEmpty()) {
                lines.add(line.trim());
            }
        }

        // 校验空文件
        if (lines.isEmpty()) {
            return new Result(false, "文件内容为空,请上传有效数据!");
        }

        // 校验首行格式(必须是 source,target,weight)
        String firstLine = lines.get(0);
        if (!"source,target,weight".equals(firstLine)) {
            return new Result(false, "文件格式错误!首行必须为:source,target,weight");
        }

        // 生成 Mermaid 横向流程图(graph LR)
        StringBuilder mermaidContent = new StringBuilder("graph LR\n");
        // 处理数据行(跳过首行)
        for (int i = 1; i < lines.size(); i++) {
            String line = lines.get(i);
            String[] parts = line.split(",");
            // 跳过格式不完整的行(至少需要 source 和 target)
            if (parts.length < 2) continue;

            String source = parts[0].trim();
            String target = parts[1].trim();
            String weight = parts.length >= 3 ? parts[2].trim() : "";

            // 拼接 Mermaid 语法:node_张三["张三"] -->|10000| node_李四["李四"]
            String sourceNode = "node_" + source;
            String targetNode = "node_" + target;
            String weightLabel = weight.isEmpty() ? "" : "|" + weight + "|";

            mermaidContent.append("  ").append(sourceNode)
                    .append("[\"").append(source).append("\"] -->")
                    .append(weightLabel).append(" ")
                    .append(targetNode).append("[\"").append(target).append("\"]\n");
        }

        // 直接生成 <div class="mermaid"> 标签(核心修改:无需 Markdown 包装)
        String dateStr = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
        String finalHtml = "<h2>" + fileName + " 转换的 Mermaid 流程图</h2>" +
                           "<blockquote>转换时间:" + dateStr + "</blockquote>" +
                           "<div class=\"mermaid\">\n" + mermaidContent.toString() + "</div>\n";

        return new Result(true, finalHtml);
    }

    // 转换结果封装类
    public static class Result {
        private boolean valid;
        private String content;

        public Result(boolean valid, String content) {
            this.valid = valid;
            this.content = content;
        }

        // getter
        public boolean isValid() { return valid; }
        public String getContent() { return content; }
    }
}
3. 重写核心控制器(MarkdownController.java)

移除所有 Flexmark 相关代码,仅保留 CSV/TXT 文件上传、校验、转换逻辑:

java 复制代码
package com.mermaid.controller;

import com.mermaid.util.CsvToMermaidUtil;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
// 新增:导入 commons-io 的 IOUtils 类
import org.apache.commons.io.IOUtils;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.List;

@WebServlet(urlPatterns = {"/", "/upload-csv"})
public class MarkdownController extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.getRequestDispatcher("/index.html").forward(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html;charset=UTF-8");
        PrintWriter out = resp.getWriter();

        try {
            if (!ServletFileUpload.isMultipartContent(req)) {
                out.write(buildErrorHtml("请通过文件上传表单提交!"));
                return;
            }

            DiskFileItemFactory factory = new DiskFileItemFactory();
            ServletFileUpload upload = new ServletFileUpload(factory);
            upload.setFileSizeMax(5 * 1024 * 1024);

            List<FileItem> items = upload.parseRequest(req);
            FileItem fileItem = null;
            for (FileItem item : items) {
                if (!item.isFormField() && "csvFile".equals(item.getFieldName())) {
                    fileItem = item;
                    break;
                }
            }

            if (fileItem == null) {
                out.write(buildErrorHtml("请选择要上传的文件!"));
                return;
            }

            String fileName = fileItem.getName();
            if (!fileName.contains(".")) {
                out.write(buildErrorHtml("仅支持上传 .csv 或 .txt 格式的文件!"));
                return;
            }
            String fileExt = fileName.substring(fileName.lastIndexOf(".")).toLowerCase();
            if (!".csv".equals(fileExt) && !".txt".equals(fileExt)) {
                out.write(buildErrorHtml("仅支持上传 .csv 或 .txt 格式的文件!"));
                return;
            }

            // ========== 核心修改:替换 readAllBytes() 为 IOUtils.toByteArray() ==========
            InputStream inputStream = fileItem.getInputStream();
            // 使用 commons-io 的 IOUtils 读取流(兼容 JDK 8)
            byte[] fileBytes = IOUtils.toByteArray(inputStream);
            String fileContent = new String(fileBytes, "UTF-8");
            // 关闭流(避免资源泄漏)
            IOUtils.close(inputStream);

            CsvToMermaidUtil.Result convertResult = CsvToMermaidUtil.validateAndConvert(fileName, fileContent);
            if (!convertResult.isValid()) {
                out.write(buildErrorHtml(convertResult.getContent()));
                return;
            }

            String finalHtml = buildFinalHtml(convertResult.getContent());
            out.write(finalHtml);

        } catch (Exception e) {
            e.printStackTrace();
            out.write(buildErrorHtml("文件处理出错:" + e.getMessage()));
        } finally {
            out.close();
        }
    }

    // 以下方法保持不变
    private String buildErrorHtml(String message) {
        return "<!DOCTYPE html>" +
                "<html lang='zh-CN'>" +
                "<head><meta charset='UTF-8'><title>处理失败</title></head>" +
                "<body style='text-align:center;margin-top:50px;'>" +
                "<h1 style='color:red;'>处理失败</h1>" +
                "<p style='font-size:18px;'>" + message + "</p>" +
                "<a href='/' style='font-size:16px;'>返回上传页面</a>" +
                "</body></html>";
    }

    private String buildFinalHtml(String mermaidHtml) {
        return "<!DOCTYPE html>" +
                "<html lang='zh-CN'>" +
                "<head>" +
                "<meta charset='UTF-8'>" +
                "<title>CSV/TXT → Mermaid 渲染结果</title>" +
                "<style>" +
                "  body { max-width: 1200px; margin: 0 auto; padding: 20px; font-family: sans-serif; }" +
                "  .mermaid { margin: 30px 0; padding: 20px; background: #f8f9fa; border-radius: 8px; }" +
                "  h2 { color: #2c3e50; border-bottom: 1px solid #eee; padding-bottom: 10px; }" +
                "  blockquote { color: #7f8c8d; border-left: 4px solid #bdc3c7; padding-left: 10px; margin: 10px 0; }" +
                "</style>" +
                "<script src='/static/js/mermaid.min.js'></script>" +
                "<script>" +
                "  mermaid.initialize({" +
                "    startOnLoad: true," +
                "    theme: 'default'," +
                "    flowchart: { useMaxWidth: true, nodeSpacing: 10, rankSpacing: 20 }" +
                "  });" +
                "</script>" +
                "</head>" +
                "<body>" +
                mermaidHtml +
                "<br/><a href='/' style='font-size:16px;'>返回上传文件页面</a>" +
                "</body></html>";
    }
}
4. 修改前端上传页面(index.html)

调整表单字段名和提示文案,仅显示 CSV/TXT 上传选项:

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>CSV/TXT → Mermaid 流程图转换工具</title>
    <style>
        body { 
            max-width: 800px; 
            margin: 0 auto; 
            padding: 50px 20px; 
            text-align: center; 
            font-family: Arial, sans-serif;
        }
        .upload-container { 
            border: 2px dashed #3498db; 
            padding: 40px; 
            border-radius: 8px; 
            margin-top: 30px; 
        }
        input[type="file"] { 
            margin: 20px 0; 
            padding: 10px; 
            font-size: 16px; 
        }
        button { 
            background: #3498db; 
            color: white; 
            border: none; 
            padding: 12px 30px; 
            font-size: 18px; 
            border-radius: 4px; 
            cursor: pointer; 
        }
        button:hover { background: #2980b9; }
        .tip { 
            color: #7f8c8d; 
            margin-top: 20px; 
            line-height: 1.6;
        }
        .format-tip {
            color: #e74c3c;
            font-weight: bold;
            margin-top: 15px;
        }
    </style>
</head>
<body>
    <h1>CSV/TXT 转换 Mermaid 流程图</h1>
    <div class="upload-container">
        <form action="/upload-csv" method="post" enctype="multipart/form-data">
            <input type="file" name="csvFile" accept=".csv,.txt" required>
            <button type="submit">上传并转换</button>
        </form>
        <p class="tip">支持格式:.csv / .txt(文件大小限制:5MB)</p>
        <p class="format-tip">文件格式要求:首行必须为 source,target,weight,后续行格式为 名称1,名称2,数值</p>
        <p class="tip">示例:<br>source,target,weight<br>张三,李四,10000<br>李四,王五,9000</p>
    </div>
</body>
</html>
5. web.xml 配置(可选,若用注解则无需修改)

如果保留 web.xml,只需确保 Servlet 映射正确,核心配置如下(无 Flexmark 相关内容):

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee 
                             http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0"
         metadata-complete="false">

    <display-name>CSVToMermaidWeb</display-name>
    <description>CSV/TXT to Mermaid Render WebApp (Tomcat 9)</description>
    
    <!-- 欢迎页 -->
    <welcome-file-list>
        <welcome-file>index.html</welcome-file>
    </welcome-file-list>

    <!-- 字符编码过滤器(解决中文乱码) -->
    <filter>
        <filter-name>CharacterEncodingFilter</filter-name>
        <filter-class>org.apache.catalina.filters.SetCharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
        <init-param>
            <param-name>ignore</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>CharacterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
        <dispatcher>REQUEST</dispatcher>
        <dispatcher>FORWARD</dispatcher>
    </filter-mapping>

    <!-- 可选:Servlet 映射(若不用 @WebServlet 注解) -->
    <servlet>
        <servlet-name>MarkdownController</servlet-name>
        <servlet-class>com.mermaid.controller.MarkdownController</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>MarkdownController</servlet-name>
        <url-pattern>/</url-pattern>
        <url-pattern>/upload-csv</url-pattern>
    </servlet-mapping>

</web-app>

二、核心修改点说明

  1. 依赖层面:彻底删除所有 Flexmark 相关依赖,仅保留 Servlet 和文件上传必要依赖,解决版本冲突问题;
  2. 文件校验 :仅允许 .csv.txt 格式文件上传,其他格式直接拦截;
  3. 转换逻辑CsvToMermaidUtil 不再生成 Markdown 代码块,而是直接返回 <div class="mermaid"> 包裹的 HTML 内容;
  4. 前端适配
    • 表单字段名从 mdFile 改为 csvFile,更贴合功能;
    • accept=".csv,.txt" 限制文件选择框仅显示 CSV/TXT 文件;
    • 提示文案更新为 CSV/TXT 格式要求,用户体验更友好;
  5. Mermaid 渲染:直接通过 CDN 引入 Mermaid 10.9,无需本地文件,避免路径问题。

三、运行验证步骤

  1. 重新打包项目:执行 mvn clean package 生成 MdWeb.war
  2. 部署到 Tomcat 9:将 war 包复制到 Tomcat 的 webapps 目录,启动 Tomcat;
  3. 访问应用:浏览器输入 http://localhost:8080/MdWeb
  4. 上传测试文件:
    • 新建 test.csv/test.txt,内容如下:

      复制代码
      source,target,weight
      张三,李四,10000
      李四,王五,9000
      王五,刘大哥,8000
    • 上传后会直接生成包含 Mermaid 流程图的页面,无需解析 Markdown。

总结

  1. 移除了所有 Flexmark 依赖,彻底解决版本冲突问题;
  2. 严格限制仅 .csv/.txt 文件可上传,聚焦核心转换功能;
  3. CsvToMermaidUtil 直接生成 <div class="mermaid"> 标签,无需 Markdown 解析环节;
  4. 前端通过 CDN 引入 Mermaid 10.9,自动渲染流程图,逻辑更简洁、运行更稳定。

整个方案去掉了不必要的 Markdown 解析环节,仅保留你需要的 CSV/TXT 转 Mermaid 核心功能,代码更轻量化,无依赖冲突问题,可直接部署运行。

相关推荐
AutumnorLiuu2 小时前
C++并发编程学习(二)—— 线程所有权和管控
java·c++·学习
Demon_Hao2 小时前
JAVA缓存的使用RedisCache、LocalCache、复合缓存
java·开发语言·缓存
踏雪羽翼2 小时前
android 解决混淆导致AGPBI: {“kind“:“error“,“text“:“Type a.a is defined multiple times
android·java·开发语言·混淆·混淆打包出现a.a
lang201509282 小时前
Tomcat Maven插件:部署与卸载的架构设计
java·tomcat·maven
serve the people2 小时前
python环境搭建 (六) Makefile 简单使用方法
java·服务器·python
重生之后端学习2 小时前
146. LRU 缓存
java·数据结构·算法·leetcode·职场和发展
萧曵 丶2 小时前
懒加载单例模式中DCL方式和原理解析
java·开发语言·单例模式·dcl
回忆是昨天里的海2 小时前
k8s部署的微服务动态扩容
java·运维·kubernetes
萧曵 丶2 小时前
单例模式 7 种实现方式对比表
java·单例模式