nodejs:express + js-mdict 网页查询英汉词典,能播放.spx 声音

向 DeepSeek R1 提问:
我想写一个Web 前端网页,后台用 nodejs + js-mdict , 实现在线查询英语单词,并能播放.spx 声音文件

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/index1.htm 中创建一个简单的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>查询英汉词典 spx</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:400; border:2px;"><iframe name="iframe" id="iframe" width="100%" height="400"> </iframe></div><div id="alist" style="float:right; width:25%; height:400; border:2px;"></div>
</div><script src="bitstring.min.js"></script> 
<script src="pcmdata.min.js"></script> 
<script src="speex.js"></script> 
<script src="script1.js"></script>
</body>
</html>

 在 public 中添加一些英汉字典的样式:
speex.js 下载网址:speex.js 是Web前端网页播放.spx声音文件所需的解码器

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

// decode Speex from mdict-js 
/** * @param bufSpx ArrayBuffer (Uint8Array) holding content of speex file (*.spx or *.ogg) */ function decodeSpx(bufSpx) { var stream, samples, st; var ogg, header, err; ogg = new Ogg(bufSpx, {file: true}); ogg.demux(); stream = ogg.bitstream(); header = Speex.parseHeader(ogg.frames[0]); console.log(header); comment = new SpeexComment(ogg.frames[1]); console.log(comment.data); st = new Speex({ quality: 8, mode: header.mode, rate: header.rate }); samples = st.decode(stream, ogg.segments); var waveData = PCMData.encode({ sampleRate: header.rate, channelCount: header.nb_channels, bytesPerSample: 2, data: samples }); // array buffer holding audio data in wav codec var bufWav = Speex.util.str2ab(waveData); // convert to a blob object var blob = new Blob([bufWav], {type: "audio/wav"}); // return a "blob://" url which can be used as a href anywhere return URL.createObjectURL(blob); }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);fetch(url).then(response => response.arrayBuffer()).then(arrayBuffer => {// 将 ArrayBuffer 转换为 Uint8Arrayconst uint8Array = new Uint8Array(arrayBuffer);   try {// 解码.spx 数据后变为.wavvar blobUrl = decodeSpx(String.fromCharCode.apply(null, uint8Array));// 创建 Audio 并播放音频var audio = new Audio(blobUrl);audio.addEventListener("canplaythrough", (event)=> {audio.play();});audio.addEventListener('error', (e) => {console.error('play error:', e);});} catch (error) {console.error('Error decoding spx data', error);}}).catch(error => {console.error('Error loading spx file', error);});}}})};});
});// 前缀查询
$(function(){$("#btn1").click(function(){$.getJSON("/prefix?word="+$("#word").val(), function(data){var items = [];$.each(data, function(i, item){if (i<=20){items[i] = '<a href="/search?word=' +item+ '" target="iframe">' +item+ "</a><br>";}});var a = items.join('\n');if (a) $('#alist').html(a);})})
});// 模糊查询
$(function(){$("#btn2").click(function(){$.getJSON("/fuzzy?word="+$("#word").val(), function(data){var items = [];$.each(data, function(i, item){if (i<=20){items[i] = '<a href="/search?word=' +item+ '" target="iframe">' +item+ "</a><br>";}});var a = items.join('\n');if (a) $('#alist').html(a);})})
});

4. 创建后端服务器

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

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

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 = 8003;// 加载MDict词典文件
//const mdict = new Mdict('path/to/your/dictionary.mdx');
const mdx = new Mdict.MDX('/path/to/your.mdx');
const mdd = new Mdict.MDD('/path/to/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;// 检查word是否合法if (word.length>50 || !isWord(word)) {return res.status(400).send('Invalid input.');}if (word) {let data = mdx.lookup(word);console.log("cha: "+ word);if(data.definition){res.send(data.definition);} else {res.status(400).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){let wordls = [];alist.forEach(function(value){wordls.push(value.keyText);});  res.json(wordls);} else {res.status(400).send('info: 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){let wordls = [];alist.forEach(function(value){wordls.push(value.keyText);});  res.json(wordls);} else {res.status(400).send('info: this 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) === '.spx'){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/ogg','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 .spx');}
});app.listen(port, () => {console.log(`Server is running on http://localhost:${port}`);
});

5. 运行项目

在项目根目录下运行以下命令启动服务器:node server/server_spx.js
然后打开浏览器,访问 http://localhost:8003/index1.htm ,你应该可以看到一个简单的词典查询页面。输入单词并点击查询按钮,页面会显示该单词的释义。

6. 部署

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

7. 进一步优化

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

  • UI 改进:可以进一步美化前端页面,添加加载动画等。

  • 安全性:在生产环境中,确保对用户输入进行验证和清理,防止 XSS 等攻击。

8. 总结

通过以上步骤,你可以实现一个简单的在线英语词典查询系统,并且能够播放 .spx 格式的音频文件。你可以根据需求进一步扩展功能,比如添加更多的词典、支持更多格式的音频文件等。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux ftrace 内核跟踪入门

文章目录 ftrace介绍开启ftraceftrace使用ftrace跟踪指定内核函数ftrace跟踪指定pid ftrace原理ftrace与stracetrace-cmd 工具KernelShark参考 ftrace介绍 Ftrace is an internal tracer designed to help out developers and designers of systems to find what is going on i…

【抽象代数】1.1. 运算及关系

集合与映射 定义1. 设 为 的子集&#xff0c;定义 到 的映射 &#xff1a; 使得 &#xff0c;称 为 到 的嵌入映射。 定义2. 设 为 的子集&#xff0c; 为 到 的映射&#xff0c; 为 到 的映射&#xff0c;如果 &#xff0c;称为的开拓&#xff0c; 为 的限制&…

pytest+request+yaml+allure 接口自动化测试全解析[手动写的跟AI的对比]

我手动写的:Python3:pytest+request+yaml+allure接口自动化测试_request+pytest+yaml-CSDN博客 AI写的:pytest+request+yaml+allure 接口自动化测试全解析 在当今的软件开发流程中,接口自动化测试扮演着至关重要的角色。它不仅能够提高测试效率,确保接口的稳定性和正确性…

数据库高安全—审计追踪:传统审计统一审计

书接上文数据库高安全—角色权限&#xff1a;权限管理&权限检查&#xff0c;从权限管理和权限检查方面解读了高斯数据库的角色权限&#xff0c;本篇将从传统审计和统一审计两方面对高斯数据库的审计追踪技术进行解读。 4 审计追踪 4.1 传统审计 审计内容的记录方式通…

第三个Qt开发实例:利用之前已经开发好的LED驱动在Qt生成的界面中控制LED2的亮和灭

前言 上一篇博文 https://blog.csdn.net/wenhao_ir/article/details/145459006 中&#xff0c;我们是直接利用GPIO子系统控制了LED2的亮和灭&#xff0c;这篇博文中我们利用之前写好的LED驱动程序在Qt的生成的界面中控制LED2的亮和灭。 之前已经在下面两篇博文中实现了LED驱动…

deepseek来讲lua

Lua 是一种轻量级、高效、可嵌入的脚本语言&#xff0c;广泛应用于游戏开发、嵌入式系统、Web 服务器等领域。以下是 Lua 的主要特点和一些基本概念&#xff1a; 1. 特点 轻量级&#xff1a;Lua 的核心非常小&#xff0c;适合嵌入到其他应用程序中。高效&#xff1a;Lua 的执…

(动态规划 leetcode377)组合求和IV

确立状态转移方程需要深入理解问题&#xff0c;合理定义子问题&#xff0c;找到边界条件(比如dp[0])&#xff0c;分析状态之间的转移关系&#xff08;dp和dp之间的关系&#xff09;&#xff0c;并进行验证。 递归是自顶向下&#xff0c;而dp是自下而上 这里是i作为目标值&…

解决aspose将Excel转成PDF中文变成方框的乱码问题

原文网址&#xff1a;解决aspose将Excel转成PDF中文变成方框的乱码问题_IT利刃出鞘的博客-CSDN博客 简介 本文介绍如何解决aspose将Excel转成PDF中文变成方框的乱码问题。 问题描述 用aspose将word、excel等转成PDF后&#xff0c;英文展示正常&#xff0c;但中文全部变成了…

Netty 核心原理与高并发场景实践

在当今的网络编程领域&#xff0c;随着互联网应用的不断发展&#xff0c;对高并发、高性能网络通信的需求日益增长。Netty 作为一款基于 Java 的异步事件驱动的网络应用框架&#xff0c;凭借其卓越的性能和丰富的功能&#xff0c;成为了实现高并发网络应用的首选工具。无论是在…

问题大集04-浏览器阻止从 本地 发起的跨域请求,因为服务器的响应头 Access-Control-Allow-Origin 设置为通配符 *

1、问题 localhost/:1 Access to XMLHttpRequest at xxx&#xff08;请求&#xff09; from origin http://localhost:xxx&#xff08;本地&#xff09; has been blocked by CORS policy: The value of the Access-Control-Allow-Origin header in the response must not be t…

判断192.168.1.0/24网络中,当前在线的ip有哪些

需求&#xff1a;判断192.168.1.0/24网络中&#xff0c;当前在线的ip有哪些&#xff0c;并编写脚本打印出来。 [rootopenEuler ~]# cat 1.sh #!/bin/bash for ip in $(seq 1 254); do ping -c 1 -W 1 "192.168.1.$ip" > /dev/null 2>&1 if [ $? …

vue-vite axios bug

axios-bug http proxy error Error: write ECONNABORTED 代码写法 一般baseURL不是单写前缀就可以了吗&#xff0c;为何要写死就不会出现以上错误&#xff0c;求解。

【Spring】_SpringBoot配置文件

目录 1.Spring Boot配置文件 1.1 Spring Boot 的配置文件类型及命名 1.2 properties和yml的优先级 2. properties配置文件 1.1 properties语法格式 1.2 自定义配置及配置文件的读取 1.3 properties的缺点 3. yml配置文件 3.1 yml语法格式 3.2 自定义配置及配置文件的…

实操给触摸一体机接入大模型语音交互

本文以CSK6 大模型开发板串口触摸屏为例&#xff0c;实操讲解触摸一体机怎样快速增加大模型语音交互功能&#xff0c;使用户能够通过语音在一体机上查询信息、获取智能回答及实现更多互动功能等。 在本文方案中通过CSK6大模型语音开发板采集用户语音&#xff0c;将语音数据传输…

深入解析 FFmpeg 的 AAC 编解码过程

深入解析 FFmpeg 的 AAC 编解码过程 —— 技术详解与代码实现 AAC(Advanced Audio Coding) 是一种高效的有损音频压缩格式,因其高压缩效率和良好的音质而被广泛应用于流媒体、广播和音频存储等领域。FFmpeg 是一个强大的多媒体处理工具,支持 AAC 的编码和解码。本文将详细…

RabbitMQ 从入门到精通:从工作模式到集群部署实战(一)

#作者&#xff1a;闫乾苓 文章目录 RabbitMQ简介RabbitMQ与VMware的关系架构工作流程RabbitMQ 队列工作模式及适用场景简单队列模式&#xff08;Simple Queue&#xff09;工作队列模式&#xff08;Work Queue&#xff09;发布/订阅模式&#xff08;Publish/Subscribe&#xff…

探索 Spring Cloud Alibaba:开启微服务架构新时代

一、引言 在当今数字化浪潮中&#xff0c;软件系统的规模和复杂度不断攀升&#xff0c;传统的单体架构逐渐难以满足快速迭代、高并发处理以及灵活扩展的需求。微服务架构应运而生&#xff0c;它将一个大型的应用拆分成多个小型、自治的服务&#xff0c;每个服务专注于特定的业务…

Linux基础命令之Nginx中的rewrite功能(重新)

一、什么是Rewrite Rewrite也称URL Rewrite&#xff0c;即URL重写&#xff0c;就是把传入Web的请求重定向到其他URL的过程。 1. URL Rewrite最常见的应用是URL伪静态化&#xff0c;是将动态页面显示为静态页面方式的一种技术。比如http://www.123.com/news/index.php?id123 使…

anaconda使用

anaconda配置镜像源&#xff1a; 引用&#xff1a;https://zhuanlan.zhihu.com/p/17776864328 # 显示所有的镜像源 conda config --show channels # 设置镜像源 conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/ conda config --add c…

DeepSeek 阐述 2025年前端发展趋势

预测2025年前端的发展趋势。首先&#xff0c;我需要考虑当前的前端 技术发展情况&#xff0c;以及近几年的变化趋势。比如&#xff0c;框架方面&#xff0c;React、Vue、Angular这些主流框架的更新方向和社区活跃度。可能用户想知道未来哪些技术会更流行&#xff0c;或者需要学…