Image Transmission between a HTML5 WebSocket Server and a Web Client

HTML5 WebSocket facilitates the communication between web browsers and local/remote servers. If you want to learn a simple websocket example, creating a WebSocket Server in C# and a Web client in JavaScript, you can refer to SuperWebSocket, which is a .NET implementation of Web Socket Server.

In this article, I would like to share how I implemented a simple WebSocket solution for image transmission based on the basic sample code of SuperWebSocket and Dynamic .NET TWAIN, especially what problem I have solved.

Here is a quick look of the WebSocket server and the HTML5 & JavaScript client.

WebSocket ServerWebSocket Client

Prerequisites

Create a .NET WebSocket Server

Create a new WinForms project, and add the following references which are located at the folder of SuperWebSocket.

WebSocket References

Add the required namespaces:

using Dynamsoft.DotNet.TWAIN;

using SuperSocket.SocketBase;
using SuperWebSocket;

In the sample code, the server is launched with port 2012, no IP specified by default. You can specify the IP address for remote access. For example:

if (!appServer.Setup("192.168.8.84", 2012)) //Setup with listening port
{
     MessageBox.Show("Failed to setup!");
     return;
}

You can use Dynamic Web TWAIN component to do some image operation. With two lines of code, I can load an image file:

bool isLoad = dynamicDotNetTwain.LoadImage("dynamsoft_logo_black.png"); // load an image
Image img = dynamicDotNetTwain.GetImage(0);

Be careful of the image format. It is PNG. If you want to display it in a Web browser, you need to convert the format from PNG to BMP:

            byte[] result;
            using (System.IO.MemoryStream stream = new System.IO.MemoryStream())
            {
                img.Save(stream, System.Drawing.Imaging.ImageFormat.Bmp);   // convert png to bmp
                result = stream.GetBuffer();
            }

It’s not done yet. The byte array contains some extra image information, which is 54 bytes in length. So the actual data length is:

int iRealLen = result.Length - 54;
byte[] image = new byte[iRealLen];

Here are some tricky things that if you just send the subset of the byte array to your Web browser, you will find the displayed image is upside-down, and the color is also incorrect.

To fix the position issue, you need to sort the bytes of original data array from bottom to top. As to the color, exchange the position of blue and red. See the code:

            int iIndex = 0;
            int iRowIndex = 0;
            int iWidth = width * 4;
            for (int i = height - 1; i >= 0; --i)
            {
                iRowIndex = i * iWidth;
                for (int j = 0; j < iWidth; j += 4)
                {
                    // RGB to BGR
                    image[iIndex++] = result[iRowIndex + j + 2 + 54]; // B
                    image[iIndex++] = result[iRowIndex + j + 1 + 54]; // G
                    image[iIndex++] = result[iRowIndex + j + 54];     // R
                    image[iIndex++] = result[iRowIndex + j + 3 + 54]; // A
                }   
            }

Now, you can send the data via:

session.Send(imageData.Data, 0, imageData.Data.Length);

Create a JavaScript Client

To receive the image as ArrayBuffer on the client side, you have to specify the binaryType after creating a WebSocket:

ws.binaryType = "arraybuffer";

Once the image data is received, draw all bytes onto a new canvas, and finally create an image element to display the canvas data:

                var imageWidth = 73, imageHeight = 73; // hardcoded width & height. 
                var byteArray = new Uint8Array(data);

                var canvas = document.createElement('canvas');
                canvas.height = imageWidth;
                canvas.width = imageHeight;
                var ctx = canvas.getContext('2d');

                var imageData = ctx.getImageData(0, 0, imageWidth, imageHeight); // total size: imageWidth * imageHeight * 4; color format BGRA
                var dataLen = imageData.data.length;
                for (var i = 0; i < dataLen; i++)
                {
                    imageData.data[i] = byteArray[i];
                }
                ctx.putImageData(imageData, 0, 0);

                // create a new element and add it to div
                var image = document.createElement('img');
                image.width = imageWidth;
                image.height = imageHeight;
                image.src = canvas.toDataURL();

                var div = document.getElementById('img');
                div.appendChild(image);

Download Sample

WebSocketImageSendSource

  • http://www.dynamsoft.com Desmond Shaw

    Thanks for your comment. It’s great! :)

  • oussama boujrida

    an easier way client side :

    var bytes = new Uint8Array(e.data);
    var binary= “”;
    var len = bytes.byteLength;
    for (var i = 0; i < len; i++) {
    binary += String.fromCharCode( bytes[ i ] )
    }
    //console.log(window.btoa( binary ));
    var img = document.getElementById("myimg");
    img.src = "data:image/png;base64,"+window.btoa( binary );

    basically converting bytes to code64string then using the feature of the img tag…server side just send a png file like that
    img.Save(stream, System.Drawing.Imaging.ImageFormat.Png);