本文使用《Signature4.0International(CCBY4.0)》许可协议,欢迎转载,或重新修改使用,但需注明出处.Signature4.0International(CCBY4.0)本文作者:苏洋创建时间:2018-12-09字数:5453字阅读时间:11分钟阅读链接:https://soulteary.com/2018/12...使用Docker和Node快速实现在线二维码解码服务。本文将介绍如何使用Docker、Node、JavaScript、Traefik完成一个简单的二维码解析服务。整个代码在300行以内。最近跟折腾文章有关的事情很多。其中,有一个现代元素其实也是挺麻烦的,那就是二维码。无论是“生成动态和静态二维码”还是“分析已经生成的二维码”,其实都不难实现。只是在日常工作中基于命令行操作不方便。于是花了一些时间实现了一个简单的QRCode在线分析工具。完成这个工具后,原来需要“打开终端,定位文件,执行命令,等待结果”简化为“打开网页,CTRL+V粘贴,稍后显示结果”,的当然,因为它提供了一个额外的接口,所以它也可以作为一个无状态的服务来使用。实现服务端核心解析逻辑核心逻辑其实很简单,就三行伪代码为例。constuploadContentByUser=req.body.files;constdecodeContent=decodeImage(uploadContentByUser);constresult=decodeQR.load(decodeContent);但在实际使用中,为了性能,我不会过度使用新语法进行代码封装,更倾向于尽可能使用“原生”的回调方式进行异步编程,避免各种“包装器”造成不必要的损失”。因为最终目标是“在浏览器中进行一次粘贴/拖动操作就完成了”。因此,我们需要对上述核心逻辑进行扩展。按照“简单项目不要过度封装”的思路,下面将代码展开到30行左右。app.post('/api/decode',multipartMiddleware,function(req,res){letfilePath='';try{if(req.files.imageFile.path)filePath=req.files.imageFile.path;}catch(e){returnres.json({code:500,content:'requestparamserror.'});}fs.readFile(filePath,function(errorWhenReadUploadFile,fileBuffer){if(errorWhenReadUploadFile)返回res.json({code:501,content:'读取上传文件错误。'});decodeImage(fileBuffer,function(errorWhenDecodeImage,image){if(errorWhenDecodeImage)returnres.json({code:502,content:errorWhenDecodeImage});letdecodeQR=newqrcodeReader();decodeQR.callback=function(errorWhenDecodeQR,result){if(errorWhenDecodeQR)returnres.json({code:503,content:errorWhenDecodeQR});if(!result)返回res.json({code:404,内容:'乱世佳人'});returnres.json({code:200,content:result.result,points:result.points});};decodeQR.decode(image.位图);});});});上面的逻辑很简单,主要做了以下几件事:接受用户上传的文件,读取用户上传的文件,解析用户上传的文件,尝试转换文件中的信息解码并反馈给用户,这依赖于一个快速的三方中间件multipartMiddleware。我主要用它来序列化上传文件的请求。源码非常简洁,100行左右。有兴趣的可以浏览一下。使用起来也非常简单。,无需配置,只需两行即可运行。constmultipart=require('connect-multiparty');constmultipartMiddleware=multipart();当然,为了能够配合客户端JavaScript来达到我们最终的目的,我们还需要一些额外的代码,比如:提供一个浏览器可以浏览的页面。这里补充一点,如果你用的是express-like的框架,一般会有一个静态方法,可以让你设置一个静态文件目录,可以在不编写路由逻辑的情况下,对外访问一些文件,例如:app.use(express.static(__dirname+'/static',{dotfiles:'ignore',etag:false,extensions:['html'],index:false,maxAge:'1h',redirect:false}));但是,这种情况下我其实只需要一个入口页面就可以满足需求,完全不需要外部资源,比如vue、react、jq、各种css框架……这时候我推荐使用fs将要显示的页面直接内存缓存,直接提供给用户的API,比如按照下面的代码编写,大概十行左右就可以满足要求。constindexCache=fs.readFileSync('./index.html');app.get('/',function(req,res){res.redirect('/index.html');});app.get('/index.html',function(req,res){res.setHeader('charset','utf-8');res.setHeader('Content-Type','text/html');res.send(索引缓存);});当然,如果你想在调试的时候像静态文件一样“热更新”这个文件,就需要把这个indexCache重写成一个方法,拦截用户请求后,每次动态读取文件,或者更高级一点,实现一个基于文件最后编辑时间戳的简单LRU缓存。实现客户端交互逻辑实现接口后,我们就完成了缺少的前端交互逻辑。因为这里没有繁重的操作,界面也很简单,既不需要jQ等库,也不需要Vue、React等框架,直接写脚本就可以了。补上我需要的界面,上半部分是数据交互的区域,下半部分是我的交互结果列表。由于页面上的元素不多,我们直接使用脚本来创建和操作元素。letuploadBox=document.createElement('textarea');uploadBox.id='upload';uploadBox.placeholder='PasteHere.';document.body.appendChild(uploadBox);letlist=document.createElement('ul');list.id='result';document.body.appendChild(list);浏览器端有3个核心操作:接受用户的拖拽粘贴图片,上传用户给的图片数据,分析服务端接口我们先实现第一个操作,拖拽粘贴丰富的交互功能,大概30个代码行可以解决战斗。functiongetFirstImage(data,isDrop){让i=0,item;让目标=isDrop?data.dataTransfer&&data.dataTransfer.files:data.clipboardData&&data.clipboardData.items;如果(!target)返回false;while(i
