如何用Blazor建立一个网络文档扫描器
了解如何轻松地将动态网络TWAIN(DWT)集成到Blazor应用程序中,这是一个用.NET构建交互式客户端Web UI的框架。
Blazor是一个用.NET构建交互式客户端Web UI的框架。1.用户可以用C#而不是JavaScript创建交互式UI。服务器端和客户端的应用逻辑都可以用.NET编写。
在这篇文章中,我们将使用动态网络TWAIN(DWT)建立一个网络文档扫描仪。动态Web TWAIN是一个为文档扫描和文档管理设计的JavaScript库。我们可以很容易地将它集成到Blazor应用程序中。
用Blazor建立一个网络文档扫描器
有两种Blazor应用程序。Blazor WebAssembly和Blazor服务器。
Blazor WebAssembly应用程序纯粹在客户端运行。C#代码文件和Razor文件被编译成.NET程序集。这些程序集和.NET运行时被下载到浏览器中。
Blazor服务器应用程序与服务器有一个实时连接(SignalR)。服务器执行应用程序的C#代码,并在客户端更新用户界面。
你可以在Blazor大学了解Blazor WebAssembly和Blazor服务器的优点和缺点。
在这篇文章中,我们将创建一个WebAssembly版本和一个服务器版本。
使用Blazor WebAssembly的网络文档扫描器
-
打开Visual Studio,创建一个Blazor WebAssembly项目。记住要选择.NET 5。
-
下载并安装Dynamic Web TWAIN。将
Resources文件夹复制到wwwroot文件夹。 -
在
Resources文件夹中修改dynamsoft.webtwain.config.js。设置ProductKey,并将Dynamsoft.DWT.AutoLoad改为false,因为我们想手动加载DWT。 -
修改
wwwroot/index.html。 将DWT的JavaScript文件包含在头部JavaScript
<script src="Resources/dynamsoft.webtwain.initiate.js"></script> <script src="Resources/dynamsoft.webtwain.config.js"></script>添加以下JavaScript,以便与正文中的C#代码互操作(在这里了解更多关于JavaScript互操作性的信息)
JavaScript
<script type="text/javascript"> var DWObject = null; function CreateDWT() { var height = 580; var width = 500; if (Dynamsoft.Lib.env.bMobile) { height = 350; width = 270; } Dynamsoft.DWT.CreateDWTObjectEx({ WebTwainId: 'dwtcontrol' }, function (obj) { DWObject = obj; DWObject.Viewer.bind(document.getElementById('dwtcontrolContainer')); DWObject.Viewer.height = height; DWObject.Viewer.width = width; DWObject.Viewer.show(); }, function (err) { console.log(err); } ); } function Scan() { if (DWObject) { DWObject.SelectSource(function () { DWObject.OpenSource(); DWObject.AcquireImage(); }, function () { console.log("SelectSource failed!"); } ); } } function LoadImage() { if (DWObject) { DWObject.LoadImageEx('', 5, function () { console.log('success'); }, function (errCode, error) { alert(error); } ); } } function Save() { DWObject.IfShowFileDialog = true; // The path is selected in the dialog, therefore we only need the file name DWObject.SaveAllAsPDF("Sample.pdf", function () { console.log('Successful!'); }, function (errCode, errString) { console.log(errString); } ); } function isDesktop() { if (Dynamsoft.Lib.env.bMobile) { return false; } else { return true; } } </script> -
添加一个新的Razor组件
Scanner.razor,代码如下:C#
@inject IJSRuntime JS @page "/scanner" <h1>Scanner</h1> @if (isDesktop) { <button class="btn btn-primary" @onclick="Scan">Scan</button> } <button class="btn btn-primary" @onclick="LoadImage">Load Image</button> <button class="btn btn-primary" @onclick="Save">Save</button> <div id="dwtcontrolContainer"></div> @code{ private Boolean isDesktop; protected override async void OnInitialized() { await CreateDWT(); isDesktop = await JS.InvokeAsync<Boolean>("isDesktop"); StateHasChanged(); } private async Task CreateDWT() { await JS.InvokeVoidAsync("CreateDWT"); } private async Task Scan() { await JS.InvokeVoidAsync("Scan"); } private async Task LoadImage() { await JS.InvokeVoidAsync("LoadImage"); } private async Task Save() { await JS.InvokeVoidAsync("Save"); } }将该组件添加到
Index.razor:JavaScript
@page "/" <h1>Hello, world!</h1> Welcome to your new app. <Scanner/> -
运行该应用程序,看看结果。当应用程序运行时,对源代码所做的任何修改都会被检测到,应用程序也会被更新。该应用程序可以从扫描仪捕捉文件,也可以加载本地图像。它可以生成一个PDF输出来保存结果。
-
我们还可以使用以下命令来运行该应用程序。
JavaScript
dotnet watch run如果手机和服务器在同一个网络上,我们可以在手机上访问该应用程序。
我们可能还需要修改
Properties/launchSettings.json,以便应用程序可以在本地网络之外访问。改变这一行。
C#
"applicationUrl": "https://localhost:5001;http://localhost:5000",改成这样。
C#
"applicationUrl": "https://+:5001;http://+:5000",由于移动电话不能直接控制扫描仪,所以扫描按钮不会被加载。设备是否是移动的,是用
Dynamsoft.Lib.env.bMobile.
使用Blazor服务器的网络文档扫描器
创建Blazor服务器版本的方法大致相同。
不同的是,没有Index.html ,而是有_Host.cshtml 。我们需要将DWT的JavaScript添加到该文件中。
网页连接到服务器。如果它失去了与服务器的连接,就会出现一个模态,网页将无法运行。
JavaScript隔离
.NET 5有一个新的功能,叫做JavaScript隔离。
JS隔离提供了以下好处2:
- 导入的JS不再污染全局命名空间。
- 一个库和组件的消费者不需要导入相关的JS。
PS:使用JavaScript隔离可能会导致网页在旧的浏览器上无法运行。
让我们在刚才的WebAssembly项目中使用JavaScript隔离。
-
在
wwwroot/js下创建一个新的JS文件,名为DWT.js,内容如下:JavaScript
var DWObject = null; export function CreateDWT() { var height = 580; var width = 500; if (Dynamsoft.Lib.env.bMobile) { height = 350; width = 270; } Dynamsoft.DWT.CreateDWTObjectEx({ WebTwainId: 'dwtcontrol' }, function (obj) { DWObject = obj; DWObject.Viewer.bind(document.getElementById('dwtcontrolContainer')); DWObject.Viewer.height = height; DWObject.Viewer.width = width; DWObject.Viewer.show(); }, function (err) { console.log(err); } ); } export function Scan() { if (DWObject) { DWObject.SelectSource(function () { DWObject.OpenSource(); DWObject.AcquireImage(); }, function () { console.log("SelectSource failed!"); } ); } } export function LoadImage() { if (DWObject) { DWObject.LoadImageEx('', 5, function () { console.log('success'); }, function (errCode, error) { alert(error); } ); } } export function Save() { DWObject.IfShowFileDialog = true; // The path is selected in the dialog, therefore we only need the file name DWObject.SaveAllAsPDF("Sample.pdf", function () { console.log('Successful!'); }, function (errCode, errString) { console.log(errString); } ); } export function isDesktop() { if (Dynamsoft.Lib.env.bMobile) { return false; } else { return true; } }我们在
wwwroot/index.html中添加的JavaScript现在可以被删除。 -
修改
Scanner.razor,以使用该JS文件:C#
@page "/scanner" @implements IAsyncDisposable @inject IJSRuntime JS <h1>Scanner</h1> @if (isDesktop) { <button class="btn btn-primary" @onclick="Scan">Scan</button> } <button class="btn btn-primary" @onclick="LoadImage">Load Image</button> <button class="btn btn-primary" @onclick="Save">Save</button> <div id="dwtcontrolContainer"></div> @code{ private Boolean isDesktop; private IJSObjectReference module; protected override async void OnAfterRender(bool firstRender) { if (firstRender) { module = await JS.InvokeAsync<IJSObjectReference>("import", "./js/DWT.js"); isDesktop = await IsDesktop(); StateHasChanged(); await CreateDWT(); } } private async Task CreateDWT() { await module.InvokeVoidAsync("CreateDWT"); } private async Task Scan() { await module.InvokeVoidAsync("Scan"); } private async Task LoadImage() { await module.InvokeVoidAsync("LoadImage"); } private async Task Save() { await module.InvokeVoidAsync("Save"); } private async Task<Boolean> IsDesktop() { return await module.InvokeAsync<Boolean>("isDesktop"); } async ValueTask IAsyncDisposable.DisposeAsync() { await module.DisposeAsync(); } }
创建一个C#封装器
我们可以更进一步,创建一个包装器来使使用更容易
-
创建一个名为
DWT的C#类文件 -
在该类文件中添加以下代码:
C#
public class DWT { private IJSObjectReference module; [Inject] IJSRuntime JS { get; set; } private DWT() { } public static async Task<DWT> CreateAsync(IJSRuntime JS) { DWT dwt = new DWT(); dwt.JS = JS; await dwt.LoadJSAsync(); return dwt; } private async Task LoadJSAsync() { module = await JS.InvokeAsync<IJSObjectReference>("import", "./js/DWT.js"); } public async Task CreateDWT() { await module.InvokeVoidAsync("CreateDWT"); } public async Task Scan() { await module.InvokeVoidAsync("Scan"); } public async Task LoadImage() { await module.InvokeVoidAsync("LoadImage"); } public async Task Save() { await module.InvokeVoidAsync("Save"); } public async Task<Boolean> IsDesktop() { return await module.InvokeAsync<Boolean>("isDesktop"); } protected virtual async ValueTask DisposeAsync() { await module.DisposeAsync(); } }由于构造函数不能是异步的,我们创建一个
CreateAsync方法来创建实例3. -
更新
Scanner.razor,以使用DWT类:C#
@inject IJSRuntime JS @page "/scanner" <h1>Scanner</h1> @if (isDesktop) { <button class="btn btn-primary" @onclick="Scan">Scan</button>} <button class="btn btn-primary" @onclick="LoadImage">Load Image</button> <button class="btn btn-primary" @onclick="Save">Save</button> <div id="dwtcontrolContainer"></div> @code{ private Boolean isDesktop; private DWT dwt; protected override async void OnAfterRender(bool firstRender) { if (firstRender) { dwt = await DWT.CreateAsync(JS); isDesktop = await dwt.IsDesktop(); StateHasChanged(); await dwt.CreateDWT(); } } private async Task Scan() { await dwt.Scan(); } private async Task LoadImage() { await dwt.LoadImage(); } private async Task Save() { await dwt.Save(); } }
源代码
参考文献
经徐立航许可,发表于DZone。点击这里查看原文