DTeam 团队日志

Doer、Delivery、Dream

小技巧:二维码的生成和测试

胡键 Posted at — Jul 23, 2018 阅读

在二维码已不是稀奇事物的今天,你的应用中有大概率会有产生二维码的需求。最近因为开始在做以太坊相关的开发,其中就有一个非常俗的需求:用户的钱包地址需要用二维码展示。

生成

在Java界,产生二维码用Google的ZXing即可。其本身没啥好说的,因为用起来很简单。在应用中简单封装一下(Groovy代码):

class QRCodeUtil {
    static void writeToStream(String walletAddress, OutputStream stream) {
        BitMatrix bitMatrix = new MultiFormatWriter().encode(walletAddress, BarcodeFormat.QR_CODE, 500, 500)
        MatrixToImageWriter.writeToStream(bitMatrix, 'png', stream)
    }
}

这样就可以在Controller中去调用了(Grails Controller):

def printWallet() {
    String wallet = params.wallet
    if (!wallet) {
        render status: BAD_REQUEST
        return
    }

    PipedInputStream inputStream = new PipedInputStream()
    try {
        inputStream.withStream {
            PipedOutputStream outputStream = new PipedOutputStream(inputStream)
            outputStream.withStream {
                QRCodeUtil.writeToStream(wallet, outputStream)
            }
            render file: inputStream, contentType: 'image/png'
        }
    } catch (Exception e) {
        log.error("Render QR image error: ", e)
        respond([message: '无法生成钱包二维码'], status: INTERNAL_SERVER_ERROR)
    }
}

请注意,这里面使用了PipedInputStream/PipedOutputStream,因为这样可以直接将两个流对接,更顺畅的完成图片的输出。

前端使用也很简单,在页面中,直接使用:< img src='地址’ >就行了。

测试

生成代码其实已经是烂大街了,这里只是用grails代码重新诠释了一下而已。但是关于二维码的测试,好像就没有那么多的例子了。在这里,我将展示一下我们的做法。

本质上就是利用ZXing的类读出图片里的字符串,下面是我们的测试工具类(Groovy):

static String readQRCode(byte[] imageBytes) {
    BinaryBitmap binaryBitmap = new BinaryBitmap(new HybridBinarizer(
            new BufferedImageLuminanceSource(ImageIO.read(new ByteArrayInputStream(imageBytes)))))
    Map<DecodeHintType, ?> hints = new HashMap<>()
    hints.put(DecodeHintType.PURE_BARCODE, true)
    Result qrCodeResult = new MultiFormatReader().decode(binaryBitmap, hints)
    return qrCodeResult.getText()
}

然后,在Grails的functional测试中实现一个端到端的测试(Spock Specification):

void "能够生成钱包二维码"() {
    setup:
    RestBuilder rest = new RestBuilder()

    when:
    String jwt = TestUtils.login(serverPort, 'user', 'user')
    response = rest.get("http://localhost:${serverPort}/地址") {
        header('Authorization', "Bearer ${jwt}")
        accept(byte[].class, 'image/*')
    }

    then:
    response.status == 200
    response.headers.getContentType() == MediaType.valueOf('image/png;charset=utf-8')
    TestUtils.readQRCode(response.body) == '……'
}

基于Grails的RestBuilder可以很方便的完API测试,这里需要注意的就是accept头的设置。如此,关于二维码的测试就完成了。

附:感谢冯宇同学提供的支持和帮助。