当前位置: 首页 > news >正文

nodejs:express + js-mdict 网页查询英汉词典,能显示图片

向 DeepSeek R1 提问:
我想写一个Web 前端网页,后台用 nodejs + js-mdict , 实现在线查询英语单词,并能显示图片

1. 项目结构

首先,创建一个项目目录,结构如下:

mydict-app/
├── public/
│   ├── index.html
│   ├── styles.css
│   └── script.js
├── server/
│   └── server.js
├── package.json
└── README.md

英汉词典文件(*.mdx , *.mdd)  已经放在 /js/testdict/ 

2. 安装依赖

在项目根目录下运行以下命令,初始化项目并安装所需的依赖:
cd mydict-app
npm init -y
npm install express
added 69 packages in 6s

npm install js-mdict
added 3 packages in 2s

3. 创建前端页面

在 public/index.html 中创建一个简单的HTML页面,包含一个输入框和三个按钮用于查询单词:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>查询英汉词典</title>
    <script src="jquery.js"></script>
    <style>
/* portrait 判断为竖屏 */
@media only screen and (orientation: portrait){
     #lab1 {display:none;}
} 
/* landscape 判断为横屏 */ 
@media only screen and (orientation: landscape){
     #lab1 {display: ;}
}    
    </style>
</head>
<body>
  <form name="form" id="form" action="search" method="GET" target="iframe">
    <label id="lab1">请输入:</label>
    <input type="text" name="word" id="word" size="30" placeholder="请输入英文单词">
    <input type="submit" name="eng_han" value="英译汉">
    <input type="button" name="btn1" id="btn1" value="前缀查询">
    <input type="button" name="btn2" id="btn2" value="模糊查询">
  </form>
  <p></p>
<div style="float:left; width:100%;">
  <div id="result" style="float:left; width:75%; height:500; border:2px;">
    <iframe name="iframe" id="iframe" width="100%" height="500"> </iframe>
  </div>
  <div id="alist" style="float:right; width:25%; height:500; border:2px;">
  </div>
</div>

<script src="script.js"></script>
</body>
</html>

 在 public 中添加一些英汉字典的样式:oalecd8e.css , oalecd8e.js , uk_pron.png, us_pron.png,
copy jquery-3.2.1.min.js pulibc\jquery.js

在 public/script.js 中编写前端逻辑:

  const iframe = $('#iframe')[0]; // 获取 iframe DOM 元素

  // 页面加载添加:监听iframe网页点击事件
  $(document).ready(function(){
    let listener = window.addEventListener('blur', function(){
      if (document.activeElement === document.getElementById('iframe')){
        $('iframe').contents().find('a').click(function(event){
          event.preventDefault();
          let a = $(this);
         if (a){
           let addr = a.attr('href');
           if (addr.indexOf('entry://')==0 && addr.indexOf('entry://#')!=0){               
                let word = encodeURIComponent(addr.substring(8));
             $.ajax({
                url: `/search?word=${word}`,
                method: 'GET',
                success: function (html) {
                  // 将 HTML 内容加载到 iframe 中
                  //$('#iframe').attr('srcdoc', html);
                let iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
                  if (html){
                    // 写入 HTML 内容
                    iframeDoc.open();
                    iframeDoc.write(html);
                    iframeDoc.close();
                  }
                },
                error: function (error) {
                    console.error('entry:请求失败:', error);
                }
             });
           } else if (addr.indexOf('sound://')==0){
             let url = "/data/" + addr.substring(8);
             let mp3 = new Audio(url);
             mp3.addEventListener("canplaythrough", (event)=> {
                 mp3.play();
             });
             mp3.addEventListener('error', (e) => {
                 console.error('play error:', e);
             });

           } else {
             console.log('href='+addr);
           }
         }
        });
 
      }
    });
  });

// 前缀查询
$(function(){
  $("#btn1").click(function(){
    $.getJSON("/prefix?word="+$("#word").val(), function(data){
      let items = [];
      $.each(data, function(i, item){
        if (i<=20){
          items[i] = '<a href="/search?word=' +item+ '" target="iframe">' +item+ "</a><br>";
        }
      });
      let a = items.join('\n');
      if (a) $('#alist').html(a);
    })
  })
});

// 模糊查询
$(function(){
  $("#btn2").click(function(){
    $.getJSON("/fuzzy?word="+$("#word").val(), function(data){
      let items = [];
      $.each(data, function(i, item){
        if (i<=20){
          items[i] = '<a href="/search?word=' +item+ '" target="iframe">' +item+ "</a><br>";
        }
      });
      let a = items.join('\n');
      if (a) $('#alist').html(a);
    })
  })
});

4. 创建后端服务器

在 server/server2.js 中编写Node.js服务器代码,使用 express 和 js-mdict 来处理查询请求:

const express = require('express');
const fs = require('fs');
const path = require('path');
const Mdict = require('js-mdict');
//console.log(Mdict);
const app = express();
const port = 8002;

// 加载MDict词典文件
//const mdict = new Mdict('path/to/your/dictionary.mdx');
const mdx = new Mdict.MDX('/js/testdict/your英汉词典插图版.mdx');
const mdd = new Mdict.MDD('/js/testdict/your英汉词典插图版.mdd');
//console.log(mdd.locate('\\goods'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
// 提供静态文件
app.use(express.static(path.join(__dirname, '../public')));

const isWord = (txt) => {
    // 只允许字母、/、空格、-
    return /^[a-zA-Z \/\-]+$/.test(txt);
};

// 处理查询请求
app.get('/search', (req, res) => {
    let word = req.query.word;
    if (word) {
        let data = mdx.lookup(word);
        console.log("cha: "+ word);
        if(data.definition){
            res.send(data.definition);
        } else {
            res.status(200).send('this word not found.');
        }
    } else {
        res.status(400).send('error: No word input');
    }
});

// 处理前缀查询请求
app.get('/prefix', (req, res) => {
    let word = req.query.word;
    // 检查word是否合法
    if (word.length>50 || !isWord(word)) {
        return res.status(400).send('Invalid input.');
    }
    if (word) {
        let alist = mdx.prefix(word);
        console.log("pre: "+ word);
        if(alist.length >0){
            let wordls = [];
            alist.forEach(function(value){
                wordls.push(value.keyText);
            });  
            res.json(wordls);
        } else {
            res.status(400).send('this word not found');
        }
    } else {
        res.status(400).send('error: No word input');
    }
});

// 处理模糊查询请求
app.get('/fuzzy', (req, res) => {
    let word = req.query.word;
    // 检查word是否合法
    if (word.length>50 || !isWord(word)) {
        return res.status(400).send('Invalid input.');
    }
    if (word) {
        let alist = mdx.fuzzy_search(word,3,1);
        console.log("fuzzy: "+ word);
        if(alist.length >0){
            let wordls = [];
            alist.forEach(function(value){
                wordls.push(value.keyText);
            });  
            res.json(wordls);
        } else {
            res.status(400).send('this word not found');
        }
    } else {
        res.status(400).send('error: No word input');
    }
});

// 指定目录
const dir1 = "/";

// 实现image文件下载,不带路径
app.get('/:fileName', (req, res, next) => {
    let path1 = '/'; 
    let fileName = req.params.fileName; // 捕获文件名
    // 检查路径中是否包含非法字符(如 ..)
    if (fileName.includes('..')) {
        return res.status(400).send('Invalid path: Path traversal is not allowed.');
    }
    //console.log(fileName);
    let extname = path.extname(fileName);
    let ext = extname.substring(1).toLowerCase();
    if (['bmp','gif','jpg','png'].includes(ext)){
      let filePath = path.join(path1, fileName);
      //console.log(filePath);
      let data = mdd.locate(filePath);
      if (data){
        console.log('key: '+ data.keyText);
        //console.log(Buffer.isBuffer(data.definition));
        
        if (data.definition){
          let binaryData = Buffer.from(data.definition, 'base64');
        //res.setHeader('Content-Type', 'application/octet-stream');
          res.set({
           'Content-Type': 'image',
           'Content-Disposition': 'attachment;',
           'Content-Length': Buffer.byteLength(binaryData)
          });
        //console.log('bytes: '+ Buffer.byteLength(binaryData));
          res.end(binaryData);
        } else {
          res.status(400).send('error: data.definition is null');
        }
      } else {
        res.status(400).send('error: data is null');
      }
    } else {
        res.status(400).send('filename.ext is not image');
    }
});

// 实现image文件下载,*/是路径
app.get('/*/:fileName', (req, res, next) => {
    let path1 = req.params[0]; // 捕获 * 匹配的部分
    let fileName = req.params.fileName; // 捕获文件名
    // 检查路径中是否包含非法字符(如 ..)
    if (fileName.includes('..')) {
        return res.status(400).send('Invalid path: Path traversal is not allowed.');
    }
    //console.log(fileName);
    let extname = path.extname(fileName);
    let ext = extname.substring(1).toLowerCase();
    if (['bmp','gif','jpg','png'].includes(ext)){
      let filePath = path.join(dir1,path1, fileName);
      //console.log(filePath);
      let data = mdd.locate(filePath);
      if (data){
        console.log('key: '+ data.keyText);
        //console.log(Buffer.isBuffer(data.definition));
        
        if (data.definition){
          let binaryData = Buffer.from(data.definition, 'base64');
        //res.setHeader('Content-Type', 'application/octet-stream');
          res.set({
           'Content-Type': 'image',
           'Content-Disposition': 'attachment;',
           'Content-Length': Buffer.byteLength(binaryData)
          });
        //console.log('bytes: '+ Buffer.byteLength(binaryData));
          res.end(binaryData);
        } else {
          res.status(400).send('error: data.definition is null');
        }
      } else {
        res.status(400).send('error: data is null');
      }
    } else {
        res.status(400).send('filename.ext is not image');
    }
});

app.listen(port, () => {
    console.log(`Server is running on http://localhost:${port}`);
});

5. 运行项目

在项目根目录下运行以下命令启动服务器:cd /js/mydict-app ;
node server/server2.js

然后打开浏览器,访问 http://localhost:8002,你应该可以看到一个简单的词典查询页面。输入单词并点击查询按钮,页面会显示该单词的释义,并显示插图。

6. 部署

你可以将这个应用部署到任何支持 Node.js 的服务器上。

7. 进一步优化

  • 错误处理: 在前端和后端添加更多的错误处理逻辑。

  • UI 改进: 使用前端框架(如React、Vue.js)来改进用户界面。

  • 缓存: 在后端添加缓存机制,提高查询速度。

  • 多词典支持: 支持加载多个词典文件,并提供切换词典的功能。

希望这些步骤能帮助你实现一个简单的在线英语单词查询应用!

相关文章:

  • 线程池有哪几种状态?
  • AI与SEO协同:智能关键词挖掘与精准部署策略
  • 以若依移动端版为基础,实现uniapp的flowable流程管理
  • 【Spring Boot】SpringBoot自动装配-Import
  • TestHubo简介与安装
  • 让编程变成一种享受-明基RD320U显示器
  • 【中间件】Pulsar集群安装
  • 神经网络新手入门(4)Transformer的创世纪(2017)
  • 0x02递推与递归
  • HTML5 起步
  • SMU Winter 2025 div1 3rd
  • JavaScript严格模式
  • NCHAR_CS和CHAR_CS,导致UNION ALL 时,提示SQL 错误 [12704] [72000]: ORA-12704: 字符集不匹配
  • Python参数的默认值进阶250216
  • Linux系统Centos安装部署nginx代理
  • win10 系统 自定义Ollama安装路径
  • 从安装软件到flask框架搭建可视化大屏(二)——创建一个flask页面,搭建可视化大屏,零基础也可以学会
  • Pointnet++改进75:添加PSConv模块 | 新型的风车状卷积
  • 频率自适应扩张卷积(FADC)详解及代码复现
  • 保持角色一致性的绘本生成AI开源项目之Story-Adapter本地部署Windows篇
  • 下辖各区密集“联手”,南京在下一盘什么样的棋?
  • 卿晨璟靓等用服刑经历“引流”,专家:将犯罪问题娱乐化会消解刑罚严肃性
  • 在美国,为什么夏季出生的孩子更容易得流感?
  • 纪念|脖子上挂着红领巾的陈逸飞
  • 中期选举后第三势力成“莎拉弹劾案”关键,菲律宾权斗更趋复杂激烈
  • 中国恒大披露清盘进展:要求债权人提交债权证明表