【问题标题】:Read all .md files, convert them to html and send them读取所有 .md 文件,将它们转换为 html 并发送它们
【发布时间】:2021-01-07 22:40:16
【问题描述】:

我使用fs读取.md格式的文件,我想把它转换成html文件。

这是我目前的代码:

fs = require('fs');
fs.readFile(__dirname + '/posts/react-v16.13.0.md', 'utf8', function (err, data) {
  if (err) {
    return console.log(err);
  }
  console.log(data);
});

该文件位于该文件夹中并具有该名称。

此函数将 .md 文件的内容放入控制台。

为了将其转换为 html,我添加了以下内容:

const showdown = require('showdown');
converter = new showdown.Converter();
...
fs = require('fs');
fs.readFile(__dirname + '/posts/react-v16.13.0.md', 'utf8', function (
  err,
  data
) {
  if (err) {
    return console.log(err);
  }
  text = data;
  html = converter.makeHtml(text);
  console.log(html);
});

它将文件作为html放在日志中,这很好。

我的问题是如果/posts/文件夹中有多个文件,如何读取和发送这些文件?

我想使用 POST 方法将它们发送到前端。

是否可以从文件夹中读取所有文件,转换并发送它们?

【问题讨论】:

  • 可以使用fs.readdir()获取文件列表。 nodejs.org/api/fs.html#fs_fs_readdir_path_options_callback
  • @AKX 我尝试了类似fs.readdir(__dirname + '/posts', 'utf8', function (err, data) { ...} 但我得到他的错误:/.../Projects/md/server/node_modules/showdown/dist/showdown.js:2459 text = text.replace(/¨/g, '¨T');
  • 这可能会对您有所帮助。 stackoverflow.com/a/10049704/1524756
  • “所有文件......发送它们”是什么意思。将它们全部连接到一个 html blob 并将其作为响应/请求正文发送?
  • 你弄丢了我,对不起,:脸红:

标签: javascript node.js express fs readfile


【解决方案1】:

从问题下方的评论线程看来,您想要执行以下操作:

  • 将所有 Markdown 文件从给定目录转换为 HTML
  • 在一个请求中全部发送
  • 可在单页应用中使用

这是一种满足所有这些要求的方法。每篇文章的 HTML 都插入到 template 元素中,其内容可以在 SPA 脚本中进行克隆和操作。

server.js

// with `.promises`, we can use `async/await`
const fs = require("fs").promises;

// ...

const getHtmlByFilename = async filename => {
  const md = await fs.readFile(
    path.join(__dirname, "posts", filename),
    "utf-8"
  );

  return converter.makeHtml(md);
};

app.get("/", async (request, response) => {
  const filenames = await fs.readdir(path.join(__dirname, "posts"));

  // we can also use Promise.all
  // to map over the filenames in parallel
  const htmls = await Promise.all(
    filenames.map(async filename => {
      const html = await getHtmlByFilename(filename);

      return { filename, html };
    })
  );

  response.send(
    htmlBoilerplate(
      htmls
        .map(
          ({ filename, html }) =>
            `<template id="${filename}">${html}</template>`
        )
        .join("\n"),
      "<h1>SPA</h1>",
      '<script src="/public/spa.js"></script>'
    )
  );
});

public/spa.js

[...document.querySelectorAll("template")].forEach(template => {
  const clone = template.content.cloneNode(true);

  const filename = template.id;

  const details = document.createElement("details");
  const summary = document.createElement("summary");

  summary.textContent = filename;

  details.appendChild(summary);
  details.appendChild(clone);

  document.querySelector(".markdown-body").appendChild(details);
});

glitch.me 演示

Source | Live

限制

  • 转换是即时完成的。如果您的流量很大,您可能需要实现一些缓存,或者可能只是单独保存 HTML 版本,并在相应的 Markdown 被编辑时触发更新。
  • 当前代码可能不是 XSS 安全的 - 这假定帖子的内容/文件名是可信的,或者您在需要时进行了适当的清理。

【讨论】:

    【解决方案2】:
    const {readdir, readFile} = require('fs');
    const showdown  = require('showdown');
    const axios = require('axios');
    
    let fileHtmlList = [];
    
    const converter = new showdown.Converter();
    
    readdir(`${__dirname}/posts`, 'utf8', (fsDirError, fileNameList) => {
        if(!fsDirError) {
            fileNameList.forEach((fileName) => {
                readFile(`${__dirname}/posts/${fileName}`, 'utf8', (fsReadError, fileContent) => {
                    if(!fsReadError) {
                        fileHtmlList.push({
                            fileName: `${__dirname}/posts/${fileName}`,
                            htmlContent: converter.makeHtml(fileContent)
                        }); 
                    } else {
                        return console.error(fsReadError);
                    }  
                });    
            });
        } else {
            return console.error(fsDirError);
        }
    });
    
    /* I'm guessing this part from your description, if the content needs to be rendered then the code needs change */
    
    let sendFrontEnd = async (data) => {
        try {
            const response = await axios.post(`urlToSend`, data);
            console.log(response);
        } catch (error) {
            console.error(error);
        }
    };
    
    fileHtmlList.forEach((item) => {
        sendFrontEnd(item.htmlContent);
    });
    

    【讨论】:

      【解决方案3】:

      我建议使用 readdir 和 readFile 的同步变体

      const basePath = __dirname + '/posts/';
      const htmls = [];
      
      fs.readdirSync(basePath).forEach(file => {
        const text = fs.readFileSync(basePath + file, 'utf8');
        htmls.push({
          file,
          html: converter.makeHtml(text)
        });
      });
      
      // upload htmls with axios/fetch/ ....
      

      【讨论】:

      • 为什么?这将成为瓶颈。
      • 我以为他想通过一个帖子请求发送所有文件,这意味着他没有那么多文件......也许我误解了
      【解决方案4】:

      试试这个js库

      <!-- Lightweight client-side loader that feature-detects and load polyfills only when necessary -->
      <script src="https://cdn.jsdelivr.net/npm/@webcomponents/webcomponentsjs@2/webcomponents-loader.min.js"></script>
      <!-- Load the element definition -->
      <script type="module" src="https://cdn.jsdelivr.net/gh/zerodevx/zero-md@1/src/zero-md.min.js"></script>
      
      
          <div class="markdown-body">
              <zero-md src="README.md"> </zero-md>
          </div>
      

      我强烈建议在 html 文件中使用零降价,因为

      • 从您的 readme.md 文件自动更新。
      • 如果您使用将自述文件转换为 html,则每次更新自述文件(或代码更多)时都必须手动转换。

      我的源代码中的完整 html

      <!DOCTYPE html>
      <html>
        <head>
          <title>API Get link Zing Mp3</title>
          <meta name="viewport" content="width=device-width, initial-scale=1.0" />
          <style>
            .markdown-body {
              box-sizing: border-box;
              min-width: 200px;
              max-width: 980px;
              margin: 0 auto;
              padding: 45px;
            }
          
            @media (max-width: 767px) {
              .markdown-body {
                padding: 15px;
              }
            }
          </style>
          <!-- Lightweight client-side loader that feature-detects and load polyfills only when necessary -->
      <script src="https://cdn.jsdelivr.net/npm/@webcomponents/webcomponentsjs@2/webcomponents-loader.min.js"></script>
      
      <!-- Load the element definition -->
      <script type="module" src="https://cdn.jsdelivr.net/gh/zerodevx/zero-md@1/src/zero-md.min.js"></script>
      
      <!-- Simply set the `src` attribute to your MD file and win -->
      
        </head>
        <body>
          <div class="markdown-body">
            <zero-md src="README.md">
            </zero-md>
          </div>
          </body>
      </html>
      

      如果你使用 nodejs,你可以在你的 readme.md 文件中添加一个路由器

      app.get('/README.md', function (req, res) {
          res.sendFile(path.join(__dirname, "README.md"));
      })
      

      【讨论】:

        猜你喜欢
        • 2021-06-08
        • 1970-01-01
        • 1970-01-01
        • 2016-08-14
        • 2022-12-10
        • 2013-01-07
        • 1970-01-01
        • 2014-01-27
        • 2020-09-15
        相关资源
        最近更新 更多