向 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)來改進用戶界面。
-
緩存: 在后端添加緩存機制,提高查詢速度。
-
多詞典支持: 支持加載多個詞典文件,并提供切換詞典的功能。
希望這些步驟能幫助你實現一個簡單的在線英語單詞查詢應用!