Creating QR Code as Base64 String using Spring Boot

Viewed 2838

I am creating a microservice using spring boot where in i have a file handling. My task is to write the QR code to image file and base64 encode to transfer over network. Because I need to write to an image file and then read and then base64 encode it , I need to first create the image file and then write to it. Creating a temperory folder and keep the file ?, create a folder in root directory and keep the file? or use the java.io.tmpdir .... Note: I have no use of this file once I encode it. Also there are lot of user to whom we will be exposing this service as rest api. I want to know what is the best way to do this.

2 Answers

If you are using the https://github.com/zxing/zxing library i.e.

    <dependency>
      <groupId>com.google.zxing</groupId>
      <artifactId>javase</artifactId>
      <version>3.2.1</version>
    </dependency>

Then something like the following will work.

@RestController
public class TestController {

    @GetMapping(value = "/test")
    public QrInfo getQrInfo() throws Exception {

        String url = "https://news.bbc.co.uk";

        int imageSize = 200;
        BitMatrix matrix = new MultiFormatWriter().encode(url, BarcodeFormat.QR_CODE,
            imageSize, imageSize);
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        MatrixToImageWriter.writeToStream(matrix, "png", bos);
        String image = Base64.getEncoder().encodeToString(bos.toByteArray()); // base64 encode

        // return QrInfo
        QrInfo qrInfo = new QrInfo();
        qrInfo.setUrl(url);
        qrInfo.setImage(image);
        return qrInfo;
    }

}


@Data // lombok for brevity
class QrInfo {

    private String url;
    private String image;

}

NOTE: This approach doesn't write any files but does it all in-memory using a ByteArrayOutputStream .

If you hit this endpoint you'll see the following: -

{
    "url": "https://news.bbc.co.uk",
    "image": "iVBORw0KGgoAAAANSUhEUgAAAMgAAADIAQAAAACFI5MzAAABGUlEQVR42u2YSw7DIAxEzYpjcFM+N+UYrErtMUkjpd2WWQQlyudtLI89JpH5a8lDHvJnUkVXmkMPKcMeAg1peo70inrpRbm/ISFDwkhNX4NUSWxEo26WVFKisgc2ArWncSO3OthJvEs0nTju/bOT+NJKzJK++c5OovJWRIob2AwNsf6YXWJ3eFGbgXS4skgEGafaDGSifVONS/ZCQ/Q2YI5l8BdSS0ImwtTezehjiM9C3FG8fbVdykft/URTeEY918hlIZZFC9Yq0Rw6ns63nyxXtkTCYK6VuJv4NKvmMdgFMBHfBbRjb8JFxgoWW04RPmKfEaY2pgcZcT/OsL3GQ5baFrUN23iZZrvJ6pKjDJFXFvL8P3jIfvIGvNX7jsCaJvEAAAAASUVORK5CYII="
}

If you paste the Base64 into e.g https://codebeautify.org/base64-to-image-converter and point camera from your phone you will see the URL.

QR Image from base64

If you rendering this out it is easy i.e. in React (any JavaScript approach will be similar).

<img src={`data:image/png;base64,${image}`} />

I like this approach as if you are e.g. generate 2FA codes you can pass down both the random secret and the QR code - useful to have both as a backup if the user doesn't have access to a mobile device.

You can do this in a designated folder in your file-system easily and once you are done with the transfer, you can remove the file. But since, this operation is exposed over a rest api, concurrency and file collision can be an issue. To avoid that you can give unique to every file-name, that way same file-name collision can be avoided.

One approach could be to UUID for writing the file-name and storing the file with this name in the file-system. It will ensure collision doesn't occur.

String fileName = UUID.randomUUID().toString();
Related