向 DeepSeek R1 提问:
我想写一个Web 前端网页,后台用 nodejs + js-mdict, 实现在线查询英语单词
首先,创建一个项目目录,结构如下:
mydict-app/
├── public/
│ ├── index.html
│ ├── styles.css
│ └── script.js
├── server/
│ └── server.js
├── package.json
└── README.md
在项目根目录下运行以下命令,初始化项目并安装所需的依赖:
cd mydict-app
npm init -y
npm install express
added 69 packages in 6s
npm install js-mdict
added 3 packages in 2s
在 public/index.html
中创建一个简单的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(){
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);
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){
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);
}
}
});
}
});
});
// 前缀查询
$(function(){
$("#btn1").click(function(){
$.getJSON("/prefix?word="+$("#word").val(), function(data){
let items = [];
$.each(data, function(i, item){
if (i<=20){
items[i] = '' +item+ "
";
}
});
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] = '' +item+ "
";
}
});
let a = items.join('\n');
if (a) $('#alist').html(a);
})
})
});
在 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.definition){
res.send(data.definition);
} else {
res.status(400).send('this 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')));
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.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 = "/";
// 实现文件下载,*/是路径
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');
}
});
// 实现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}`);
});
在项目根目录下运行以下命令启动服务器:node server/server.js
或者 node server/server2.js
然后打开浏览器,访问 http://localhost:8000
,你应该可以看到一个简单的词典查询页面。输入单词并点击查询按钮,页面会显示该单词的释义。
你可以将这个应用部署到任何支持 Node.js 的服务器上。
错误处理: 在前端和后端添加更多的错误处理逻辑。
UI 改进: 使用前端框架(如React、Vue.js)来改进用户界面。
缓存: 在后端添加缓存机制,提高查询速度。
多词典支持: 支持加载多个词典文件,并提供切换词典的功能。
希望这些步骤能帮助你实现一个简单的在线英语单词查询应用!