如你所见,这又是一篇水文

前几天培训的时候老师讲了一个用javaweb中的二维码模块生成二维码,不过只能保存到本地。

正好最近自己在研究Node.js,于是决定使用Node自己实现一个在线生成二维码的接口。

引入模块

这里我在GitHub上找到了一个qr-image模块,研究一番之后发现用法不难,于是就决定用它了。

配置路由

为了方便说明,这里解释一下我之前的路由配置,下面编辑后的是router/index.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const express = require('express')
const app = express()
const randPicRouter = require('../apis/randPic')
const qrCodeRouter = require('../apis/qrcode')

var port = process.env.PORT || 8081

const server = app.listen(port,() => {
const port = server.address().port
console.log(`Server is running at http://localhost:${port}/`)
})

app.use(express.static('./public/'))
app.use('/randPic',randPicRouter)
app.use('/qrcode',qrCodeRouter)

module.exports = app

这里我使用了express框架的路由模块,并把各个路径抽离成单独的模块,然后再在对应模块中导出router对象。

其中的app.use('/qrcode',qrCodeRouter)是本次新加入的路由。

编写接口

我们先来看一下开发者给的示例:

1
2
3
4
5
6
var qr = require('qr-image');
router.get('/qr', function(){
var code = qr.image('http://www.google.com', { type: 'png' });
res.setHeader('Content-type', 'image/png'); //sent qr image to client side
code.pipe(res);
});

接口用法:

  • qr.image(text, [ec_level | options]) — Readable stream with image data;
  • qr.imageSync(text, [ec_level | options]) — string with image data. (Buffer for png);
  • qr.svgObject(text, [ec_level | options]) — object with SVG path and size;
  • qr.matrix(text, [ec_level]) — 2D array of booleans. Y is indexed first (e.g. [y][x] NOT [x][y]), [0][0] is the top left, and true means black.

Options参数:

  • ec_level — default M.
  • type — image type. Possible values png (default), svg, pdf and eps.
  • size (png and svg only) — size of one module in pixels. Default 5 for png and undefined for svg.
  • margin — white space around QR image in modules. Default 4 for png and 1 for others.
  • customize (only png) — function to customize qr bitmap before encoding to PNG.
  • parse_url (experimental, default false) — try to optimize QR-code for URLs.

根据上面的接口文档,我决定使用url,sizemargin这三个参数,并将size和margin设为可选参数,不传时将会传入默认值。

下面是代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
qrCodeRouter.get('/',((req, res) => {
let url = req.query.url
let size = Number.parseInt(req.query.size)
let margin = Number.parseInt(req.query.margin)
if (url === undefined){
res.setHeader('Content-type', 'text/plain')
res.status(400)
res.send("请求参数有误,请检查url参数是否正确!")
return 1
}
let code = qrImage.image(url,{
type: 'png',
size: size || 5,
margin: margin || 4,
parse_url: true
})
res.setHeader('Content-type', 'image/png')
res.status(200)
code.pipe(res)
}))

这里使用了||运算符来判断size和margin是否为空,如果为空这赋值为默认值。

另外,为了更好的支持url的生成,将 parse_url设为true。

关于其中的pipe函数

上面使用了code.pipe(res)来将二维码图片写到响应数据中。由于不是很了解Node.js的I/O操作,这里我查了一下资料:

想要把 Readable 的数据写到 Writable,就必须先手动的将数据读入内存,然后写入 Writable。换句话说,每次传递数据时,都需要写如下的模板代码:

1
2
3
4
readable.on('readable', (err) => {
if(err) throw err
writable.write(readable.read())
})

为了方便使用,Node.js 提供了 pipe() 方法,让我们可以优雅的传递数据:

1
readable.pipe(writable)

大概理解就是,一个输出流会有一个pipe函数,用于传入一个输入流对象。而上面的res对象则是实现了writable接口的响应体,我们可以把它传入到pipe函数中,这样就实现了把图片输入到响应体中。

接口示例

写到这里时我已经将接口部署到了Heroku,下面则是一个直接引用该接口的二维码(由于Heroku的服务器在美国,所以加载可能不是很快):

该图片的链接为:https://api.revincx.icu/qrcode?url=https://blog.revincx.icu/&size=10&margin=2

关于我自己的接口站请见:https://api.revincx.icu

目前还尚在学习中,接口数量较少,目标是有朝一日打造Revincx全家桶(bushi