node 创建静态web服务器(下)(处理异步获取数据的两种方式)
接上一章。
上一章我们说创建的静态web服务器只能识别html,css,js文件,功能较为单一,且图片格式为text/html,这是不合理的。
本章,我们将解决该问题。
这里,我们先准备好一个json文件
{".323":"text/h323",".3gp":"video/3gpp",".aab":"application/x-authoware-bin",".aam":"application/x-authoware-map",".aas":"application/x-authoware-seg",".acx":"application/internet-property-stream",".ai":"application/postscript",".aif":"audio/x-aiff",".aifc":"audio/x-aiff",".aiff":"audio/x-aiff",".als":"audio/X-Alpha5",".amc":"application/x-mpeg",".ani":"application/octet-stream",".apk":"application/vnd.android.package-archive",".asc":"text/plain",".asd":"application/astound",".asf":"video/x-ms-asf",".asn":"application/astound",".asp":"application/x-asap",".asr":"video/x-ms-asf",".asx":"video/x-ms-asf",".au":"audio/basic",".avb":"application/octet-stream",".avi":"video/x-msvideo",".awb":"audio/amr-wb",".axs":"application/olescript",".bas":"text/plain",".bcpio":"application/x-bcpio",".bin ":"application/octet-stream",".bld":"application/bld",".bld2":"application/bld2",".bmp":"image/bmp",".bpk":"application/octet-stream",".bz2":"application/x-bzip2",".c":"text/plain",".cal":"image/x-cals",".cat":"application/vnd.ms-pkiseccat",".ccn":"application/x-cnc",".cco":"application/x-cocoa",".cdf":"application/x-cdf",".cer":"application/x-x509-ca-cert",".cgi":"magnus-internal/cgi",".chat":"application/x-chat",".class":"application/octet-stream",".clp":"application/x-msclip",".cmx":"image/x-cmx",".co":"application/x-cult3d-object",".cod":"image/cis-cod",".conf":"text/plain",".cpio":"application/x-cpio",".cpp":"text/plain",".cpt":"application/mac-compactpro",".crd":"application/x-mscardfile",".crl":"application/pkix-crl",".crt":"application/x-x509-ca-cert",".csh":"application/x-csh",".csm":"chemical/x-csml",".csml":"chemical/x-csml",".css":"text/css",".cur":"application/octet-stream",".dcm":"x-lml/x-evm",".dcr":"application/x-director",".dcx":"image/x-dcx",".der":"application/x-x509-ca-cert",".dhtml":"text/html",".dir":"application/x-director",".dll":"application/x-msdownload",".dmg":"application/octet-stream",".dms":"application/octet-stream",".doc":"application/msword",".docx":"application/vnd.openxmlformats-officedocument.wordprocessingml.document",".dot":"application/msword",".dvi":"application/x-dvi",".dwf":"drawing/x-dwf",".dwg":"application/x-autocad",".dxf":"application/x-autocad",".dxr":"application/x-director",".ebk":"application/x-expandedbook",".emb":"chemical/x-embl-dl-nucleotide",".embl":"chemical/x-embl-dl-nucleotide",".eps":"application/postscript",".epub":"application/epub+zip",".eri":"image/x-eri",".es":"audio/echospeech",".esl":"audio/echospeech",".etc":"application/x-earthtime",".etx":"text/x-setext",".evm":"x-lml/x-evm",".evy":"application/envoy",".exe":"application/octet-stream",".fh4":"image/x-freehand",".fh5":"image/x-freehand",".fhc":"image/x-freehand",".fif":"application/fractals",".flr":"x-world/x-vrml",".flv":"flv-application/octet-stream",".fm":"application/x-maker",".fpx":"image/x-fpx",".fvi":"video/isivideo",".gau":"chemical/x-gaussian-input",".gca":"application/x-gca-compressed",".gdb":"x-lml/x-gdb",".gif":"image/gif",".gps":"application/x-gps",".gtar":"application/x-gtar",".gz":"application/x-gzip",".h":"text/plain",".hdf":"application/x-hdf",".hdm":"text/x-hdml",".hdml":"text/x-hdml",".hlp":"application/winhlp",".hqx":"application/mac-binhex40",".hta":"application/hta",".htc":"text/x-component",".htm":"text/html",".html":"text/html",".hts":"text/html",".htt":"text/webviewhtml",".ice":"x-conference/x-cooltalk",".ico":"image/x-icon",".ief":"image/ief",".ifm":"image/gif",".ifs":"image/ifs",".iii":"application/x-iphone",".imy":"audio/melody",".ins":"application/x-internet-signup",".ips":"application/x-ipscript",".ipx":"application/x-ipix",".isp":"application/x-internet-signup",".it":"audio/x-mod",".itz":"audio/x-mod",".ivr":"i-world/i-vrml",".j2k":"image/j2k",".jad":"text/vnd.sun.j2me.app-descriptor",".jam":"application/x-jam",".jar":"application/java-archive",".java":"text/plain",".jfif":"image/pipeg",".jnlp":"application/x-java-jnlp-file",".jpe":"image/jpeg",".jpeg":"image/jpeg",".jpg":"image/jpeg",".jpz":"image/jpeg",".js":"application/x-javascript",".jwc":"application/jwc",".kjx":"application/x-kjx",".lak":"x-lml/x-lak",".latex":"application/x-latex",".lcc":"application/fastman",".lcl":"application/x-digitalloca",".lcr":"application/x-digitalloca",".lgh":"application/lgh",".lha":"application/octet-stream",".lml":"x-lml/x-lml",".lmlpack":"x-lml/x-lmlpack",".log":"text/plain",".lsf":"video/x-la-asf",".lsx":"video/x-la-asf",".lzh":"application/octet-stream",".m13":"application/x-msmediaview",".m14":"application/x-msmediaview",".m15":"audio/x-mod",".m3u":"audio/x-mpegurl",".m3url":"audio/x-mpegurl",".m4a":"audio/mp4a-latm",".m4b":"audio/mp4a-latm",".m4p":"audio/mp4a-latm",".m4u":"video/vnd.mpegurl",".m4v":"video/x-m4v",".ma1":"audio/ma1",".ma2":"audio/ma2",".ma3":"audio/ma3",".ma5":"audio/ma5",".man":"application/x-troff-man",".map":"magnus-internal/imagemap",".mbd":"application/mbedlet",".mct":"application/x-mascot",".mdb":"application/x-msaccess",".mdz":"audio/x-mod",".me":"application/x-troff-me",".mel":"text/x-vmel",".mht":"message/rfc822",".mhtml":"message/rfc822",".mi":"application/x-mif",".mid":"audio/mid",".midi":"audio/midi",".mif":"application/x-mif",".mil":"image/x-cals",".mio":"audio/x-mio",".mmf":"application/x-skt-lbs",".mng":"video/x-mng",".mny":"application/x-msmoney",".moc":"application/x-mocha",".mocha":"application/x-mocha",".mod":"audio/x-mod",".mof":"application/x-yumekara",".mol":"chemical/x-mdl-molfile",".mop":"chemical/x-mopac-input",".mov":"video/quicktime",".movie":"video/x-sgi-movie",".mp2":"video/mpeg",".mp3":"audio/mpeg",".mp4":"video/mp4",".mpa":"video/mpeg",".mpc":"application/vnd.mpohun.certificate",".mpe":"video/mpeg",".mpeg":"video/mpeg",".mpg":"video/mpeg",".mpg4":"video/mp4",".mpga":"audio/mpeg",".mpn":"application/vnd.mophun.application",".mpp":"application/vnd.ms-project",".mps":"application/x-mapserver",".mpv2":"video/mpeg",".mrl":"text/x-mrml",".mrm":"application/x-mrm",".ms":"application/x-troff-ms",".msg":"application/vnd.ms-outlook",".mts":"application/metastream",".mtx":"application/metastream",".mtz":"application/metastream",".mvb":"application/x-msmediaview",".mzv":"application/metastream",".nar":"application/zip",".nbmp":"image/nbmp",".nc":"application/x-netcdf",".ndb":"x-lml/x-ndb",".ndwn":"application/ndwn",".nif":"application/x-nif",".nmz":"application/x-scream",".nokia-op-logo":"image/vnd.nok-oplogo-color",".npx":"application/x-netfpx",".nsnd":"audio/nsnd",".nva":"application/x-neva1",".nws":"message/rfc822",".oda":"application/oda",".ogg":"audio/ogg",".oom":"application/x-AtlasMate-Plugin",".p10":"application/pkcs10",".p12":"application/x-pkcs12",".p7b":"application/x-pkcs7-certificates",".p7c":"application/x-pkcs7-mime",".p7m":"application/x-pkcs7-mime",".p7r":"application/x-pkcs7-certreqresp",".p7s":"application/x-pkcs7-signature",".pac":"audio/x-pac",".pae":"audio/x-epac",".pan":"application/x-pan",".pbm":"image/x-portable-bitmap",".pcx":"image/x-pcx",".pda":"image/x-pda",".pdb":"chemical/x-pdb",".pdf":"application/pdf",".pfr":"application/font-tdpfr",".pfx":"application/x-pkcs12",".pgm":"image/x-portable-graymap",".pict":"image/x-pict",".pko":"application/ynd.ms-pkipko",".pm":"application/x-perl",".pma":"application/x-perfmon",".pmc":"application/x-perfmon",".pmd":"application/x-pmd",".pml":"application/x-perfmon",".pmr":"application/x-perfmon",".pmw":"application/x-perfmon",".png":"image/png",".pnm":"image/x-portable-anymap",".pnz":"image/png",".pot,":"application/vnd.ms-powerpoint",".ppm":"image/x-portable-pixmap",".pps":"application/vnd.ms-powerpoint",".ppt":"application/vnd.ms-powerpoint",".pptx":"application/vnd.openxmlformats-officedocument.presentationml.presentation",".pqf":"application/x-cprplayer",".pqi":"application/cprplayer",".prc":"application/x-prc",".prf":"application/pics-rules",".prop":"text/plain",".proxy":"application/x-ns-proxy-autoconfig",".ps":"application/postscript",".ptlk":"application/listenup",".pub":"application/x-mspublisher",".pvx":"video/x-pv-pvx",".qcp":"audio/vnd.qcelp",".qt":"video/quicktime",".qti":"image/x-quicktime",".qtif":"image/x-quicktime",".r3t":"text/vnd.rn-realtext3d",".ra":"audio/x-pn-realaudio",".ram":"audio/x-pn-realaudio",".rar":"application/octet-stream",".ras":"image/x-cmu-raster",".rc":"text/plain",".rdf":"application/rdf+xml",".rf":"image/vnd.rn-realflash",".rgb":"image/x-rgb",".rlf":"application/x-richlink",".rm":"audio/x-pn-realaudio",".rmf":"audio/x-rmf",".rmi":"audio/mid",".rmm":"audio/x-pn-realaudio",".rmvb":"audio/x-pn-realaudio",".rnx":"application/vnd.rn-realplayer",".roff":"application/x-troff",".rp":"image/vnd.rn-realpix",".rpm":"audio/x-pn-realaudio-plugin",".rt":"text/vnd.rn-realtext",".rte":"x-lml/x-gps",".rtf":"application/rtf",".rtg":"application/metastream",".rtx":"text/richtext",".rv":"video/vnd.rn-realvideo",".rwc":"application/x-rogerwilco",".s3m":"audio/x-mod",".s3z":"audio/x-mod",".sca":"application/x-supercard",".scd":"application/x-msschedule",".sct":"text/scriptlet",".sdf":"application/e-score",".sea":"application/x-stuffit",".setpay":"application/set-payment-initiation",".setreg":"application/set-registration-initiation",".sgm":"text/x-sgml",".sgml":"text/x-sgml",".sh":"application/x-sh",".shar":"application/x-shar",".shtml":"magnus-internal/parsed-html",".shw":"application/presentations",".si6":"image/si6",".si7":"image/vnd.stiwap.sis",".si9":"image/vnd.lgtwap.sis",".sis":"application/vnd.symbian.install",".sit":"application/x-stuffit",".skd":"application/x-Koan",".skm":"application/x-Koan",".skp":"application/x-Koan",".skt":"application/x-Koan",".slc":"application/x-salsa",".smd":"audio/x-smd",".smi":"application/smil",".smil":"application/smil",".smp":"application/studiom",".smz":"audio/x-smd",".snd":"audio/basic",".spc":"application/x-pkcs7-certificates",".spl":"application/futuresplash",".spr":"application/x-sprite",".sprite":"application/x-sprite",".sdp":"application/sdp",".spt":"application/x-spt",".src":"application/x-wais-source",".sst":"application/vnd.ms-pkicertstore",".stk":"application/hyperstudio",".stl":"application/vnd.ms-pkistl",".stm":"text/html",".svg":"image/svg+xml",".sv4cpio":"application/x-sv4cpio",".sv4crc":"application/x-sv4crc",".svf":"image/vnd",".svg":"image/svg+xml",".svh":"image/svh",".svr":"x-world/x-svr",".swf":"application/x-shockwave-flash",".swfl":"application/x-shockwave-flash",".t":"application/x-troff",".tad":"application/octet-stream",".talk":"text/x-speech",".tar":"application/x-tar",".taz":"application/x-tar",".tbp":"application/x-timbuktu",".tbt":"application/x-timbuktu",".tcl":"application/x-tcl",".tex":"application/x-tex",".texi":"application/x-texinfo",".texinfo":"application/x-texinfo",".tgz":"application/x-compressed",".thm":"application/vnd.eri.thm",".tif":"image/tiff",".tiff":"image/tiff",".tki":"application/x-tkined",".tkined":"application/x-tkined",".toc":"application/toc",".toy":"image/toy",".tr":"application/x-troff",".trk":"x-lml/x-gps",".trm":"application/x-msterminal",".tsi":"audio/tsplayer",".tsp":"application/dsptype",".tsv":"text/tab-separated-values",".ttf":"application/octet-stream",".ttz":"application/t-time",".txt":"text/plain",".uls":"text/iuls",".ult":"audio/x-mod",".ustar":"application/x-ustar",".uu":"application/x-uuencode",".uue":"application/x-uuencode",".vcd":"application/x-cdlink",".vcf":"text/x-vcard",".vdo":"video/vdo",".vib":"audio/vib",".viv":"video/vivo",".vivo":"video/vivo",".vmd":"application/vocaltec-media-desc",".vmf":"application/vocaltec-media-file",".vmi":"application/x-dreamcast-vms-info",".vms":"application/x-dreamcast-vms",".vox":"audio/voxware",".vqe":"audio/x-twinvq-plugin",".vqf":"audio/x-twinvq",".vql":"audio/x-twinvq",".vre":"x-world/x-vream",".vrml":"x-world/x-vrml",".vrt":"x-world/x-vrt",".vrw":"x-world/x-vream",".vts":"workbook/formulaone",".wav":"audio/x-wav",".wax":"audio/x-ms-wax",".wbmp":"image/vnd.wap.wbmp",".wcm":"application/vnd.ms-works",".wdb":"application/vnd.ms-works",".web":"application/vnd.xara",".wi":"image/wavelet",".wis":"application/x-InstallShield",".wks":"application/vnd.ms-works",".wm":"video/x-ms-wm",".wma":"audio/x-ms-wma",".wmd":"application/x-ms-wmd",".wmf":"application/x-msmetafile",".wml":"text/vnd.wap.wml",".wmlc":"application/vnd.wap.wmlc",".wmls":"text/vnd.wap.wmlscript",".wmlsc":"application/vnd.wap.wmlscriptc",".wmlscript":"text/vnd.wap.wmlscript",".wmv":"audio/x-ms-wmv",".wmx":"video/x-ms-wmx",".wmz":"application/x-ms-wmz",".wpng":"image/x-up-wpng",".wps":"application/vnd.ms-works",".wpt":"x-lml/x-gps",".wri":"application/x-mswrite",".wrl":"x-world/x-vrml",".wrz":"x-world/x-vrml",".ws":"text/vnd.wap.wmlscript",".wsc":"application/vnd.wap.wmlscriptc",".wv":"video/wavelet",".wvx":"video/x-ms-wvx",".wxl":"application/x-wxl",".x-gzip":"application/x-gzip",".xaf":"x-world/x-vrml",".xar":"application/vnd.xara",".xbm":"image/x-xbitmap",".xdm":"application/x-xdma",".xdma":"application/x-xdma",".xdw":"application/vnd.fujixerox.docuworks",".xht":"application/xhtml+xml",".xhtm":"application/xhtml+xml",".xhtml":"application/xhtml+xml",".xla":"application/vnd.ms-excel",".xlc":"application/vnd.ms-excel",".xll":"application/x-excel",".xlm":"application/vnd.ms-excel",".xls":"application/vnd.ms-excel",".xlsx":"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",".xlt":"application/vnd.ms-excel",".xlw":"application/vnd.ms-excel",".xm":"audio/x-mod",".xml":"text/plain",".xml":"application/xml",".xmz":"audio/x-mod",".xof":"x-world/x-vrml",".xpi":"application/x-xpinstall",".xpm":"image/x-xpixmap",".xsit":"text/xml",".xsl":"text/xml",".xul":"text/xul",".xwd":"image/x-xwindowdump",".xyz":"chemical/x-pdb",".yz1":"application/x-yz1",".z":"application/x-compress",".zac":"application/x-zaurus-zac",".zip":"application/zip",".json":"application/json"}
该文件包含绝大多数文件格式。
一、同步读取文件
我们将上一章中创建的model文件下的getMime.js内容替换掉,这里,我们采用文件读取的方式,代码如下:
const getMime = (fs, extName) => { // 注意,这里 fs 模块 要从对应文件中传过来 let data = fs.readFileSync('./mime.json'); // 同步读取文件 let Mimes = JSON.parse(data.toString()); // 将读取到的文件转为字符串,再转为json格式 return Mimes[extName] || 'text/html'; // 返回对应文件格式,如果没有,则返回 text/html 格式 } module.exports = { getMime }
这样是可以实现我们想要的效果的,对应的文件有对应的格式,但,这节的重点不在这里,为什么我们不采用异步的方式调用呢?而采用readFileSync 这种同步的读取方式?
二、回调函数处理异步获取数据
下面,我们来看一段代码:
const fs = require('fs'); console.log('1'); fs.readFile('./mime.json', (err, data) => { if (err) { console.log(404); return false; } else { console.log('2'); return data; } }); console.log('3');
这是一段异步读取文件的代码,我们来看看执行结果
如果是同步执行,则顺序应为 1 2 3 ,但node采用的是非阻塞式 i/o,事件驱动的方式,宏观上实现了并行,而实际上,node 仅仅只使用一个线程
所以,如果我们采用异步的方式读取json文件,实际上是拿不到数据的,会返回 undefined。这就是为什么采用 readFileSync 同步读取的原因。
那么,如果,我们使用异步的方式读取,怎么才能读取到呢,这时,就需要使用回调的方式来处理异步。
代码如下:
const fs = require('fs'); let getMime = (callback) => { fs.readFile('./mime.json', (err, data) => { callback(data); // 将 data 作为参数传递给回调函数 }) } getMime((result) => { console.log(result); })
这样,就算是异步的读取,我们也可以通过回调的方式获取到数据。
对于我们创建的静态web服务器来说,实现代码如下: 注意:粉色区域是实现的重点
// 这是 model 下的 getMime.js 文件
const getMime = (fs,extname,callback)=>{ fs.readFile('./mime.json',(err,data)=>{ if(err){ console.log('mime.json文件不存在'); return false; }else{ let Mimes = JSON.parse(data.toString()); let result = Mimes[extname] || 'text/html'; callback(result); } }) } module.exports = { getMime }
const http = require('http'); const fs = require('fs'); const path = require('path'); const url = require('url'); const mimeModel = require('./model/getMime'); let server = http.createServer((req, res) => { let pathName = url.parse(req.url).pathname; if (pathName == '/') { pathName = '/index.html'; } let extName = path.extname(pathName); if (pathName != '/favicon.ico') { fs.readFile('static/' + pathName, (err, data) => { if (err) { fs.readFile('static/404.html', (err, data404) => { if (err) { console.log('404'); } else { res.writeHead(404, {'Content-Type': 'text/html;charset=utf-8'}); res.write(data404); res.end(); } }) } else { let mime = mimeModel.getMime(fs,extName,(mime)=>{ res.writeHead(200, {'Content-Type': "" + mime + ";charset='utf-8'"}); res.write(data); res.end(); }) } }) } }) server.listen(3000);
三、events 模块方法 EventEmitter() 处理异步
我们还可以通过node提供的 events 模块来处理异步,达到同样的效果
来看下面这段代码:
const events = require('events'); // 引入 events 模块 let EventsEmitter = new events.EventEmitter(); // 实例化 EventEmitter 方法 // 监听 接收 EventsEmitter.on('to_parent', (data) => { console.log('接收到了数据'); console.log(data); }) // 触发 传递 setTimeout(() => { console.log('开始广播...'); EventsEmitter.emit('to_parent', '发送的数据'); }, 1000)
这里与vue中的事件中心是一样的,emit 方法广播,on 方法接收广播,注意,触发的事件名 需一致
1s 后
好了,基础知识准备好了,下面,我们来用这种方式实现我们的静态web服务器
const getMime = (fs, extName, EventEmitter) => { // 这里需要传递过来三个参数,第一个是fs模块,第二个是文件后缀名,第三个是实例化的events方法EventEmitter fs.readFile('./mime.json', (err, data) => { if (err) { console.log(err); return false; } else { let Mimes = JSON.parse(data.toString()); let result = Mimes[extName] || 'text/html'; EventEmitter.emit('to_mime', result); // 将数据广播出去 } }) } module.exports = { getMime }
const http = require('http'); const fs = require('fs'); const path = require('path'); const url = require('url'); const events = require('events'); // 引入 events 模块 const mimeModel = require('./model/getMime'); let EventEmitter = new events.EventEmitter(); // 实例化 events 模块的 EventEmitter 方法 let server = http.createServer((req, res) => { let pathName = url.parse(req.url).pathname; if (pathName == '/') { pathName = '/index.html'; } let extName = path.extname(pathName); if (pathName != '/favicon.ico') { fs.readFile('static/' + pathName, (err, data) => { if (err) { fs.readFile('static/404.html', (err, data404) => { if (err) { console.log('404'); } else { res.writeHead(404, {'Content-Type': 'text/html;charset=utf-8'}); res.write(data404); res.end(); } }) } else { mimeModel.getMime(fs, extName, EventEmitter); // 执行 getMime EventEmitter.on('to_mime', (mimes) => { // 监听,接收传递过来的数据 res.writeHead(200, {'Content-Type': "" + mimes + ";charset='utf-8'"}); res.end(data); }); } }) } }) server.listen(3000);
关于
Node.js EventEmitter 的具体内容,请参考:http://www.runoob.com/nodejs/nodejs-event.html
注意:这里如果这样写会报错
mimeModel.getMime(fs, extName, EventEmitter);
EventEmitter.on('to_mime', (mimes) => { // 监听,接收传递过来的数据
res.writeHead(200, {'Content-Type': "" + mimes + ";charset='utf-8'"});
res.write(data);
res.end();
});
网上有一种解释是:没有等回调完成就发送请求。
我觉得是有一定道理的,如果我们这样写,就不会有这个问题:
mimeModel.getMime(fs, extName, EventEmitter); EventEmitter.on('to_mime', (mimes) => { res.writeHead(200, {'Content-Type': "" + mimes + ";charset='utf-8'"}) }); res.write(data); res.end();
具体原因,因为笔者本身水平有限,这里并不能给大家准确的答案,如果,大家有较为标准的答案,请在下方评论留言。