Create a barcode reader using HTML5, JavaScript, and Jetty

Updated in 2021:

  1. The Dynamsoft Barcode Reader now has a client-side JavaScript SDK which makes it possible to read barcodes from within the browser. Learn about the JavaScript SDK
  2. The Dynamsoft Barcode Reader now has mobile native SDKs supporting Android and iOS.
  3. The Dynamsoft Barcode Reader now has an official Java SDK.
  4. The article and the code in the GitHub repo is revamped to use the official Java SDK and support the latest APIs and browsers.

This article talks about creating a HTML5 barcode reader using websocket in Java, which is inspired by the article[^1]. We can transmit the real-time camera stream to a remote server using HTML5 in Web browsers and decode the barcode image with Dynamsoft Barcode Reader SDK on the server that running on Windows, Linux or Mac.

If you would like to scan barcodes and QR codes at the client side of a browser, please read blog: Build a Barcode and QR Code Scanner Using JavaScript and HTML5

Prerequisites

  • Dynamsoft Barcode Reader: Java SDK - available for Windows, Linux and Mac.
  • WebSocket Server: Embedded Jetty - a Java HTTP (Web) Server and Java Servlet container.

How to Open Webcam or Mobile Cameras with HTML5

In HTML5, MediaDevices.getUserMedia() prompts the user for camera permission. Here we use it to show the camera video stream:

//https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia
if (navigator.mediaDevices.getUserMedia) {
    navigator.mediaDevices.getUserMedia(constraints).then(function(mediaStream) {
        try {
            videoElement.srcObject = mediaStream;
        } 
        catch (error) {
            videoElement.src = window.URL.createObjectURL(mediaStream);
        }
            videoElement.play();
        })
        .catch(function(error) {
            console.log(error.name + ": " + error.message);
        });
    }
else {
    alert("Unsupported");
    console.log("MediaDevices.getUserMedia not supported");
}

Camera Preview Data for Transmission

When using Webcam or cameras in Web browsers, we can capture preview image as base64 String and then convert it to binary:

Convert canvas to base64 String:

data = canvas.toDataURL('image/png', 1.0);

Convert base64 to binary:

// stackoverflow: http://stackoverflow.com/questions/4998908/convert-data-uri-to-file-then-append-to-formdata/5100158
function dataURItoBlob(dataURI) {
    // convert base64/URLEncoded data component to raw binary data held in a string
    var byteString;
    if (dataURI.split(',')[0].indexOf('base64') >= 0)
        byteString = atob(dataURI.split(',')[1]);
    else
        byteString = unescape(dataURI.split(',')[1]);

    // separate out the mime component
    var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];

    // write the bytes of the string to a typed array
    var ia = new Uint8Array(byteString.length);
    for (var i = 0; i < byteString.length; i++) {
        ia[i] = byteString.charCodeAt(i);
    }

    return new Blob([ia], {type:mimeString});
}

// convert base64 to binary
newblob = dataURItoBlob(data);
ws.send(newblob);

We can wrap received binary data as BufferedImage on server-side:

ByteArrayInputStream in = new ByteArrayInputStream(data);
BufferedImage bi;
try {
    bi = ImageIO.read(in);
    File file = new File(mFile);
    // save client data to local image file
    ImageIO.write(bi, "PNG", file);

} catch (IOException e1) {
    // TODO Auto-generated catch block
    e1.printStackTrace();
}

How to Use WebSocket to Send and Receive Data

WebSocket Client in JavaScript

Create WebSocket:

// create websocket
var ws = new WebSocket("wss://192.168.8.84:88");

ws.onopen = function() {

};

ws.onmessage = function (evt) {

};

ws.onclose = function() {

};

ws.onerror = function(err) {

};

Set an interval time for sending camera stream:

// scan barcode
function scanBarcode() {
  intervalId = window.setInterval(function() {
    if (!isConnected || isPaused) {
        return;
    }

    var data = null, newblob = null;

    ctx.drawImage(videoElement, 0, 0, videoWidth, videoHeight);
    // convert canvas to base64
    data = canvas.toDataURL('image/png', 1.0);
    // convert base64 to binary
    newblob = dataURItoBlob(data);
    ws.send(newblob);

  }, 200);
  console.log("create id: " + intervalId);
}

Do not forget to empty interval events if you stop scanning barcode:

window.clearInterval(intervalId);

WebSocket Server in Java

Create the server. Since the getUserMedia API requires HTTPS, we need to enable HTTPS. Please note that on iOS, self-signed certs do NOT work for secure websocket connections.

public class WebSocketServer {

    public static void main(String[] args) throws Exception {
        Server server = new Server();
        //Set static folder
        File f = new File("www");
           
        ServletContextHandler context = new ServletContextHandler(
                    ServletContextHandler.SESSIONS);
        context.setContextPath("/");
        context.setBaseResource(Resource.newResource(f.toURI()));
        context.setWelcomeFiles(new String[] { "index.html" });

        ServletHolder holderPwd = new ServletHolder("default",
                    DefaultServlet.class);
        holderPwd.setInitParameter("dirAllowed", "true");
        context.addServlet(holderPwd, "/");
        
        //Configure handlers with HTTPS support
        HandlerCollection handlerList = new HandlerCollection();
        handlerList.setHandlers(new Handler[]{new WSHandler(),context});
        server.setHandler(handlerList);
        
        HttpConfiguration http_config = new HttpConfiguration();
        http_config.setSecureScheme("https");
        http_config.setSecurePort(8443);

        HttpConfiguration https_config = new HttpConfiguration(http_config);
        https_config.addCustomizer(new SecureRequestCustomizer());
        
        SslContextFactory sslContextFactory = new SslContextFactory();
        // You can congifure your own key. See https://stackoverflow.com/questions/37967362/websocket-over-ssl-in-embedded-jetty-9
        sslContextFactory.setKeyStorePath("keystore.jks");
        sslContextFactory.setKeyStorePassword("password1");

        ServerConnector wsConnector = new ServerConnector(server);
        wsConnector.setHost(null);
        wsConnector.setPort(8080);
        server.addConnector(wsConnector);

        ServerConnector wssConnector = new ServerConnector(server,
                new SslConnectionFactory(sslContextFactory,
                    HttpVersion.HTTP_1_1.asString()),
                new HttpConnectionFactory(https_config)); // THIS WAS MISSING

        wssConnector.setHost(null);
        wssConnector.setPort(8443);
        server.addConnector(wssConnector);
        
        server.setStopTimeout(0);
        server.start();
        server.join();
    }
}

Configure policies:

@WebSocket
public class WSHandler extends WebSocketHandler {

    @Override
    public void configure(WebSocketServletFactory factory) {
        // TODO Auto-generated method stub
        factory.setCreator(new BarcodeCreator());

        // configuration
        factory.getPolicy().setMaxBinaryMessageBufferSize(1024 \* 1024);
        factory.getPolicy().setMaxTextMessageBufferSize(1024 \* 1024);
        factory.getPolicy().setMaxTextMessageSize(1024 \* 1024);
        factory.getPolicy().setMaxBinaryMessageSize(1024 \* 1024);
    }
}

Create WebSocket:

@WebSocket
public class BarcodeSocket {

    @OnWebSocketClose
    public void onClose(int statusCode, String reason) {

    }

    @OnWebSocketError
    public void onError(Throwable t) {

    }

    @OnWebSocketConnect
    public void onConnect(Session session) {

    }

    @OnWebSocketMessage
    public void onMessage(byte[] data, int off, int len) {
        //receive video frame, decode and send decoded results to the client
    }

}

public class BarcodeCreator implements WebSocketCreator {

    @Override
    public Object createWebSocket(ServletUpgradeRequest arg0, ServletUpgradeResponse arg1) {
        // TODO Auto-generated method stub
        return new BarcodeSocket();
    }

}

How to Read Barcode in Java with Dynamsoft Barcode Reader SDK

With the Java SDK, we can read barcodes with a few lines of code:

private BarcodeReader dbr;
public BarcodeSocket() {
    //......
    try {
        dbr = new BarcodeReader();
    } catch (BarcodeReaderException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}
private String readBarcode(String fileName) {
    StringBuilder sb = new StringBuilder();
    TextResult[] results = null;
    try {
        results = dbr.decodeFile(fileName, "");
    } catch (BarcodeReaderException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    for (TextResult result : results) {
        sb.append("Value: ");
        sb.append(result.barcodeText);
    }
    return sb.toString();
}

Testing HTML5 Barcode Reader in Chrome for Android and Windows

Chrome for Android

mobile android camera

mobile barcode reader

Chrome for Windows

desktop barcode reader

Source Code

https://github.com/dynamsoftsamples/HTML5-Webcam-Barcode-Reader