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" size="30" placeholder="请输入英文单词">
    <input type="submit" name="eng_han" value="英译汉">
  </form>
  <p></p>
<div id="result">
  <iframe name="iframe" id="iframe" width="90%" height="450">     
  </iframe>
</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 元素
  const iframeDoc = iframe.contentDocument || iframe.contentWindow.document;

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

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

4. 创建后端服务器

在 server/server.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 = 8000;

// 加载MDict词典文件
//const mdict = new Mdict('path/to/your/dictionary.mdx');
const mdict = new Mdict.MDX('/js/testdict/oale8.mdx');
//const mdd = new Mdict.MDD('/js/testdict/oale8.mdd');

app.use(express.json());
app.use(express.urlencoded({ extended: false }));
// 提供静态文件
app.use(express.static(path.join(__dirname, '../public')));

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

// 指定目录
const dir1 = "/js/testdict/data";

// 实现文件下载,*/是路径
app.get('/data/*/:fileName', (req, res, next) => {
    let path1 = req.params[0]; // 捕获 * 匹配的部分
    let fileName = req.params.fileName; // 捕获文件名
    // 检查路径中是否包含非法字符(如 ..)
    if (path1.includes('..') || fileName.includes('..')) {
        return res.status(400).send('Invalid path: Path traversal is not allowed.');
    }
  
    let filePath = path.join(dir1,path1, fileName);
    console.log(filePath);
    let stats = fs.statSync(filePath);
    if(stats.isFile()){
      if (path.extname(fileName) === '.mp3'){
        res.set({
         'Content-Type': 'application/octet-stream',
         'Content-Disposition': 'attachment;',
         'Content-Length': stats.size
        });
        //'Content-Disposition': 'attachment; filename='+encodeURI(fileName),
        res.download(filePath);
      } else {
         res.status(400).send('filename.ext is not .mp3');
      }
    } else {
      res.end('404');
    }
});

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

如何直接读取.MDD文件中的音频数据,编写了升级版 server2.js 如下

const http = require('http');
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/oale8.mdx');
const mdd = new Mdict.MDD('/js/testdict/oale8.mdd');
//console.log(mdd.locate('\\us\\salute__us_1.mp3'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
// 提供静态文件
app.use(express.static(path.join(__dirname, '../public')));

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

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

// 实现文件下载,*/是路径
app.get('/data/*/:fileName', (req, res, next) => {
    let path1 = req.params[0]; // 捕获 * 匹配的部分
    let fileName = req.params.fileName; // 捕获文件名
    // 检查路径中是否包含非法字符(如 ..)
    if (path1.includes('..') || fileName.includes('..')) {
        return res.status(400).send('Invalid path: Path traversal is not allowed.');
    }
    if (path.extname(fileName) === '.mp3'){
      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': 'audio/mpeg',
           '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 .mp3');
    }
});

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

5. 运行项目

在项目根目录下运行以下命令启动服务器:node server/server.js
或者 node server/server2.js

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

6. 部署

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

7. 进一步优化

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

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

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

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/964595.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

使用Pygame制作“太空侵略者”游戏

1. 前言 在 2D 游戏开发中&#xff0c;“太空侵略者”是一款入门难度适中、却能覆盖多种常见游戏机制的项目&#xff1a; 玩家控制飞船&#xff08;Player&#xff09;左右移动&#xff0c;发射子弹。敌人&#xff08;Enemy&#xff09;排列成一行或多行&#xff0c;从屏幕顶…

Linux find 命令 | grep 命令 | 查找 / 列出文件或目录路径 | 示例

注&#xff1a;本文为 “Linux find 命令 | grep 命令使用” 相关文章合辑。 未整理去重。 如何在 Linux 中查找文件 作者&#xff1a; Lewis Cowles 译者&#xff1a; LCTT geekpi | 2018-04-28 07:09 使用简单的命令在 Linux 下基于类型、内容等快速查找文件。 如果你是 W…

JVM 四虚拟机栈

虚拟机栈出现的背景 由于跨平台性的设计&#xff0c;Java的指令都是根据栈来设计的。不同平台CPU架构不同&#xff0c;所以不能设计为基于寄存器的。优点是跨平台&#xff0c;指令集小&#xff0c;编译器容易实现&#xff0c;缺点是性能下降&#xff0c;实现同样的功能需要更多…

排序算法与查找算法

1.十大经典排序算法 我们希望数据以一种有序的形式组织起来&#xff0c;无序的数据我们要尽量将其变得有序 一般说来有10种比较经典的排序算法 简单记忆为Miss D----D小姐 时间复杂度 &#xff1a;红色<绿色<蓝色 空间复杂度&#xff1a;圆越大越占空间 稳定性&…

Spring理论知识(Ⅴ)——Spring Web模块

Spring的组成 Spring由20个核心依赖组成&#xff0c;这20个核心依赖可以分为6个核心模块 Spring Web模块简介 众所周知&#xff0c;Java目前最大的一个用途就是作为Web应用的服务端&#xff08;Java Web&#xff09; Spring又是JavaEE中使用最广泛的开发框架&#xff0…

(10) 如何获取 linux 系统上的 TCP 、 UDP 套接字的收发缓存的默认大小,以及代码范例

&#xff08;1&#xff09; 先介绍下后面的代码里要用到的基础函数&#xff1a; 以及&#xff1a; &#xff08;2&#xff09; 接着给出现代版的 读写 socket 参数的系统函数 &#xff1a; 以及&#xff1a; &#xff08;3&#xff09; 给出 一言的 范例代码&#xff0c;获取…

车载软件架构 --- 软件定义汽车面向服务架构的应用迁移

我是穿拖鞋的汉子&#xff0c;魔都中坚持长期主义的汽车电子工程师。 老规矩&#xff0c;分享一段喜欢的文字&#xff0c;避免自己成为高知识低文化的工程师&#xff1a; 简单&#xff0c;单纯&#xff0c;喜欢独处&#xff0c;独来独往&#xff0c;不易合同频过着接地气的生活…

【高级篇 / IPv6】(7.2) ❀ 04. 在60E上配置ADSL拨号宽带上网(IPv4) ❀ FortiGate 防火墙

【简介】除了单位用户以外&#xff0c;大部分个人用户目前使用的仍然是30E、50E、60E系列防火墙&#xff0c;固件无法达到目前最高版本7.6&#xff0c;这里以最常用的60E为例&#xff0c;演示固件版本7.2下实现ADSL拨号宽带的IPv6上网。由于内容比较多&#xff0c;文章分上、下…

鼠标拖尾特效

文章目录 鼠标拖尾特效一、引言二、实现原理1、监听鼠标移动事件2、生成拖尾元素3、控制元素生命周期 三、代码实现四、使用示例五、总结 鼠标拖尾特效 一、引言 鼠标拖尾特效是一种非常酷炫的前端交互效果&#xff0c;能够为网页增添独特的视觉体验。它通常通过JavaScript和C…

OpenEuler学习笔记(十六):搭建postgresql高可用数据库环境

以下是在OpenEuler系统上搭建PostgreSQL高可用数据环境的一般步骤&#xff0c;通常可以使用流复制&#xff08;Streaming Replication&#xff09;或基于Patroni等工具来实现高可用&#xff0c;以下以流复制为例&#xff1a; 安装PostgreSQL 配置软件源&#xff1a;可以使用O…

机器学习--2.多元线性回归

多元线性回归 1、基本概念 1.1、连续值 1.2、离散值 1.3、简单线性回归 1.4、最优解 1.5、多元线性回归 2、正规方程 2.1、最小二乘法 2.2、多元一次方程举例 2.3、矩阵转置公式与求导公式 2.4、推导正规方程0的解 2.5、凸函数判定 成年人最大的自律就是&#xff1a…

(done) MIT6.S081 2023 学习笔记 (Day7: LAB6 Multithreading)

网页&#xff1a;https://pdos.csail.mit.edu/6.S081/2023/labs/thread.html (任务1教会了你如何用 C 语言调用汇编&#xff0c;编译后链接即可) 任务1&#xff1a;Uthread: switching between threads (完成) 在这个练习中&#xff0c;你将设计一个用户级线程系统中的上下文切…

AWS配置邮件服务器给其他邮箱发邮件的方法

一、前言 公司系统用AWS平台的邮件服务器&#xff0c;如果想给新收件人发邮件&#xff0c;就需要特殊配置下才行&#xff1b;不然发不出去。 二、步骤 1.登录AWS平台&#xff0c;登录地址&#xff08;藏的很深&#xff0c;不太好搜到&#xff0c;参数也不能删&#xff09; …

kaggle视频行为分析1st and Future - Player Contact Detection

这次比赛的目标是检测美式橄榄球NFL比赛中球员经历的外部接触。您将使用视频和球员追踪数据来识别发生接触的时刻&#xff0c;以帮助提高球员的安全。两种接触&#xff0c;一种是人与人的&#xff0c;另一种是人与地面&#xff0c;不包括脚底和地面的&#xff0c;跟我之前做的这…

42【文件名的编码规则】

我们在学习的过程中&#xff0c;写出数据或读取数据时需要考虑编码类型 火山采用&#xff1a;UTF-16 易语言采用&#xff1a;GBK php采用&#xff1a;UTF-8 那么我们写出的文件名应该是何种编码的&#xff1f;比如火山程序向本地写出一个“测试.txt”&#xff0c;理论上这个“测…

全栈开发:使用.NET Core WebAPI构建前后端分离的核心技巧(一)

目录 cors解决跨域 依赖注入使用 分层服务注册 缓存方法使用 内存缓存使用 缓存过期清理 缓存存在问题 分布式的缓存 cors解决跨域 前后端分离已经成为一种越来越流行的架构模式&#xff0c;由于跨域资源共享(cors)是浏览器的一种安全机制&#xff0c;它会阻止前端应用…

JVM执行流程与架构(对应不同版本JDK)

直接上图&#xff08;对应JDK8以及以后的HotSpot&#xff09; 这里主要区分说明一下 方法区于 字符串常量池 的位置更迭&#xff1a; 方法区 JDK7 以及之前的版本将方法区存放在堆区域中的 永久代空间&#xff0c;堆的大小由虚拟机参数来控制。 JDK8 以及之后的版本将方法…

【玩转 Postman 接口测试与开发2_014】第11章:测试现成的 API 接口(下)——自动化接口测试脚本实战演练 + 测试集合共享

《API Testing and Development with Postman》最新第二版封面 文章目录 3 接口自动化测试实战3.1 测试环境的改造3.2 对列表查询接口的测试3.3 对查询单个实例的测试3.4 对新增接口的测试3.5 对修改接口的测试3.6 对删除接口的测试 4 测试集合的共享操作4.1 分享 Postman 集合…

登录认证(5):过滤器:Filter

统一拦截 上文我们提到&#xff08;登录认证&#xff08;4&#xff09;&#xff1a;令牌技术&#xff09;&#xff0c;现在大部分项目都使用JWT令牌来进行会话跟踪&#xff0c;来完成登录功能。有了JWT令牌可以标识用户的登录状态&#xff0c;但是完整的登录逻辑如图所示&…

基于Spring Security 6的OAuth2 系列之七 - 授权服务器--自定义数据库客户端信息

之所以想写这一系列&#xff0c;是因为之前工作过程中使用Spring Security OAuth2搭建了网关和授权服务器&#xff0c;但当时基于spring-boot 2.3.x&#xff0c;其默认的Spring Security是5.3.x。之后新项目升级到了spring-boot 3.3.0&#xff0c;结果一看Spring Security也升级…