Create a barcode reader using HTML5, JavaScript, and Jetty
Updated in 2021:
- 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
- The Dynamsoft Barcode Reader now has mobile native SDKs supporting Android and iOS.
- The Dynamsoft Barcode Reader now has an official Java SDK.
- 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
Chrome for Windows
Source Code
https://github.com/dynamsoftsamples/HTML5-Webcam-Barcode-Reader