Last week, we developed a RESTful service using .NET C# and Twain.Wia.Sane.Scanner library to scan documents from TWAIN, WIA, SANE, and eSCL scanners. This week, we will repurpose the C# code to construct a Blazor WebAssembly application that digitizes documents directly from a web browser, all without the need for JavaScript.
Prerequisites
-
Install the Dynamsoft Service on a machine that is connected to scanners and hosts a web service.
-
Modify the host IP address to make it publicly accessible.
You can test it by visiting
http://[host-ip]:18626/DWTAPI/Scanners
in a browser. If you see the following page, it means the Dynamsoft Service is successfully installed and running. Request a free trial license for Dynamsoft Service.
Steps to Create a Web-Based Document Scanning Application Using Blazor WebAssembly
In the following paragraphs, we will demonstrate the process of constructing a web-based document digitization application from scratch. For clarity, we will start by establishing an empty Blazor WebAssembly project, which will only encompass an Index.razor
file.
Initiate the Blazor Project
-
Create the Blazor project using Visual Studio or the command line:
dotnet new blazorwasm-empty -o blazorwasm-empty-project
-
Install the NuGet package:
dotnet add package Twain.Wia.Sane.Scanner --version 1.1.0
Implement the App UI in HTML
We construct the app UI as follow:
<div id="loading-indicator" class="loading-indicator">
<div class="spinner"></div>
</div>
<div class="connection">
<div class="row">
<div>
<label>License key: </label>
<div class="filler"></div>
<input type="text" placeholder="">
</div>
</div>
<div class="row">
<div>
<label>Enter host address: </label>
<div class="filler"></div>
<input type="text" id="host" placeholder="">
</div>
</div>
</div>
<div class="container" id="dwt">
<div class="row">
<div>
<button onclick="getDevices()">Get Devices</button>
<select id="sources">
</select>
<button onclick="acquireImage()">Scan Documents</button>
</div>
</div>
<div class="row">
<div class="full-img">
<img id="scanner-image">
</div>
</div>
<div class="row">
<div class="thumb-bar" id="thumb-bar">
<div class="thumb-box" id="thumb-box">
</div>
</div>
</div>
</div>
The loading-indicator
is used to show the loading animation. The connection
section is for users to enter the license key and host address. The dwt
section is for the document scanning part.
Convert HTML to Razor
Razor is a markup syntax that enables developers to embed C# code within HTML, facilitating the creation of dynamic web pages using C#. In the following sections, we will convert the above HTML code to comply with the Razor syntax.
How to import a .NET library into a Blazor project?
Import the Twain.Wia.Sane.Scanner
namespace in the Index.razor
file to use the ScannerController
class:
@page "/"
@using Twain.Wia.Sane.Scanner
@code {
private ScannerController scannerController = new ScannerController();
}
How to append options to select element in Blazor?
To append options to a select element in a Blazor app using C#, we typically use the Blazor rendering system, rather than manipulating the DOM directly with JavaScript.
<button @onclick="GetDevices">Get Devices</button>
<select id="sources" @bind="selectedValue">
@foreach (var device in devices)
{
<option value="@device["name"].ToString()">@device["name"].ToString()</option>
}
</select>
@code {
private bool isLoading = false;
private string host = "http://127.0.0.1:18622";
private List<Dictionary<string, object>> devices = new List<Dictionary<string, object>>();
private string licenseKey = "LICENSE-KEY";
private string jobId = "";
private string selectedValue { get; set; } = string.Empty;
private List<string> imageUrls { get; set; } = new List<string>();
public async Task GetDevices()
{
isLoading = true;
try
{
devices = await scannerController.GetDevices(host);
if (devices.Count >= 0)
{
selectedValue = devices[0]["name"].ToString();
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
isLoading = false;
}
}
- The
host
andlicenseKey
need to be modified to your own values. - The
List<Dictionary<string, object>> devices
stores the options for the select element. - The
@foreach
directive is used to render each option in the devices list. - The
@bind
directive is used for two-way data binding. It bind the selected value to theselectedValue
property.
How to show the loading indicator?
To show a loading indicator when pressing the button and hide it once the function completes, we use a boolean flag that tracks whether data is being loaded. Based on the value of this flag, the loading indicator can be rendered conditionally.
<div id="loading-indicator" class="loading-indicator" style="@(isLoading ? "display: flex;" : "display: none;")">
<div class="spinner"></div>
</div>
How to acquire the image stream and display it in the browser?
The image stream is of type byte[]
. To display such a byte[]
image stream in an <img>
element in Blazor, it can be converted to a Base64 string
. Using the data: URL
scheme, this string can be embedded directly into the src
attribute of the <img>
element. All image URLs are stored in the imageUrls
list. By clicking on a thumbnail, the corresponding image will be displayed in the <img>
element.
<button @onclick="AcquireImage">Scan Documents</button>
<div class="row">
<div class="full-img">
<img id="scanner-image" src="@imageDataUrl">
</div>
</div>
<div class="row">
<div class="thumb-bar" id="thumb-bar">
<div class="thumb-box" id="thumb-box">
@foreach (var url in imageUrls)
{
<img src="@url" @onclick="() => OnImageClick(url)" />
}
</div>
</div>
</div>
@code {
public async Task AcquireImage()
{
if (devices.Count == 0)
{
return;
}
int selectedIndex = devices.FindIndex(device => device["name"].ToString() == selectedValue);
if (selectedIndex < 0) return;
var parameters = new Dictionary<string, object>
{
{"license", licenseKey},
{"device", devices[selectedIndex]["device"]}
};
parameters["config"] = new Dictionary<string, object>
{
{"IfShowUI", false},
{"PixelType", 2},
{"Resolution", 200},
{"IfFeederEnabled", false},
{"IfDuplexEnabled", false}
};
jobId = await scannerController.ScanDocument(host, parameters);
byte[] bytes = await scannerController.GetImageStream(host, jobId);
imageDataUrl = $"data:image/png;base64,{Convert.ToBase64String(bytes)}";
AddImageUrl(imageDataUrl);
}
private void AddImageUrl(string url)
{
imageUrls.Add(url);
}
private void OnImageClick(string url)
{
imageDataUrl = url;
}
}
Online Demo Deployed on GitHub Pages
After completing the Blazor project, we can deploy it to GitHub Pages.
- Create a new repository on GitHub.
- Push the Blazor project to the repository.
- Navigate to the repository:
Settings > Actions > General > Workflow permissions
and enableRead and write permissions
. -
Create a new workflow file named
main.yml
in the.github/workflows
folder. Replacedotnet-blazor-digitize-document
with your own repository name.
name: blazorwasm on: push: branches: [ main ] pull_request: branches: [ main ] workflow_dispatch: jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Setup .NET Core SDK uses: actions/setup-dotnet@v2 with: dotnet-version: '7.0.x' include-prerelease: true - name: Publish .NET Core Project run: dotnet publish blazorwasm-empty-project.csproj -c Release -o release --nologo - name: Change base-tag in index.html from / to dotnet-blazor-digitize-document run: sed -i 's/<base href="\/" \/>/<base href="\/dotnet-blazor-digitize-document\/" \/>/g' release/wwwroot/index.html - name: copy index.html to 404.html run: cp release/wwwroot/index.html release/wwwroot/404.html - name: Add .nojekyll file run: touch release/wwwroot/.nojekyll - name: Commit wwwroot to GitHub Pages uses: JamesIves/github-pages-deploy-action@3.7.1 with: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} BRANCH: gh-pages FOLDER: release/wwwroot
To test your local Dynamsoft service on a GitHub page, install ngrok and run
ngrok http 18622
to expose the local port18622
to the internet.
Online Demo
Visit https://yushulx.me/blazor-barcode-mrz-document-scanner/ and use your own license key.
The host address can either be Dynamsoft's test URL: https://demo.scannerproxy.com/ddm/18626
or your personal one.
Source Code
https://github.com/yushulx/blazor-barcode-mrz-document-scanner