如何用Blazor建立一个网络文档扫描器(详细指南)

156 阅读5分钟

如何用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的网络文档扫描器

  1. 打开Visual Studio,创建一个Blazor WebAssembly项目。记住要选择.NET 5。

  2. 下载并安装Dynamic Web TWAIN。将Resources 文件夹复制到wwwroot 文件夹。

  3. Resources 文件夹中修改dynamsoft.webtwain.config.js 。设置ProductKey ,并将Dynamsoft.DWT.AutoLoad 改为false,因为我们想手动加载DWT。

  4. 修改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>
    
  5. 添加一个新的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/>
    
  6. 运行该应用程序,看看结果。当应用程序运行时,对源代码所做的任何修改都会被检测到,应用程序也会被更新。该应用程序可以从扫描仪捕捉文件,也可以加载本地图像。它可以生成一个PDF输出来保存结果。

  7. 我们还可以使用以下命令来运行该应用程序。

    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隔离。

  1. 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现在可以被删除。

  2. 修改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#封装器

我们可以更进一步,创建一个包装器来使使用更容易

  1. 创建一个名为DWT 的C#类文件

  2. 在该类文件中添加以下代码:

    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.

  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();
         }
     }
    

源代码

github.com/xulihang/Bl…

参考文献

  1. docs.microsoft.com/en-us/aspne…
  2. docs.microsoft.com/en-us/aspne…
  3. stackoverflow.com/questions/2…

经徐立航许可,发表于DZone。点击这里查看原文