scanonweb 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,578 @@
1
+ /*!
2
+ * scanonweb v1.0.1
3
+ * ScanOnWeb - 扫描控件 JavaScript SDK,用于与本地扫描服务程序通信
4
+ * https://www.brainysoft.cn
5
+ *
6
+ * Copyright (c) 2025 BrainySoft
7
+ * Licensed under the MIT license
8
+ */
9
+ /**
10
+ * ScanOnWeb - 扫描控件 JavaScript SDK
11
+ * https://www.brainysoft.cn 扫描控件官方网站,如需更多文档帮助请访问官网
12
+ * @version 1.0.1
13
+ * @author BrainySoft
14
+ * @license MIT
15
+ */
16
+
17
+ /**
18
+ * 扫描控件类 - 用于与本地扫描服务程序通信
19
+ * @class ScanOnWeb
20
+ */
21
+ class ScanOnWeb {
22
+ constructor() {
23
+ // 扫描配置参数信息,后续扫描仪开始扫描前会根据这个配置信息来进行扫描设置
24
+ this.scaner_work_config = {
25
+ showUI: false,
26
+ // 是否显示扫描控件工作界面,如果为false则不显示扫描控件工作界面
27
+ dpi_x: 300,
28
+ // dpi 分辨率x,一般的图像扫描300dpi就够了
29
+ dpi_y: 300,
30
+ // dpi 分辨率y
31
+ deviceIndex: 0,
32
+ // 选中的扫描仪硬件设备id索引,如果有多个扫描仪设备,这个值就是用来选择哪个设备进行扫描的
33
+ showDialog: false,
34
+ // 是否显示设备内置对话框
35
+ autoFeedEnable: true,
36
+ // 是否使用自动进纸器(需要设备支持才能正常工作)
37
+ autoFeed: false,
38
+ // 是否自动装填纸张(需要设备支持才能正常工作)
39
+ dupxMode: false,
40
+ // 是否使用双面扫描模式(需要设备支持才能正常工作)
41
+ autoDeskew: false,
42
+ // 是否使用自动纠偏模式(需要设备支持才能正常工作)
43
+ autoBorderDetection: false,
44
+ // 是否使用自动边框检测(需要设备支持才能正常工作)
45
+ colorMode: "RGB",
46
+ // 色彩模式,RGB为彩色模式,BW是黑白模式 ,GRAY是灰色模式
47
+ transMode: "memory" // 数据传输模式,memory,file,native 这三种配置值,默认为memory模式,大部分设备都是使用这种模式
48
+ };
49
+ this.h5socket = null;
50
+ this.imageCount = 0; // 扫描结果图像总数
51
+
52
+ // 尝试连接websocket服务器,注意连接成功哪个是通过回调实现的
53
+ this.tryConnect();
54
+ }
55
+
56
+ /**
57
+ * 通过连接多个websocket server端口返回一个可用的websocket连接对象,主要用于防止本地端口被占用的情况
58
+ * @param {string[]} wssUrls - WebSocket服务器URL数组
59
+ * @returns {Promise} WebSocket连接Promise
60
+ */
61
+ getConnectedServer(wssUrls) {
62
+ console.log("尝试连接托盘扫描服务websocket服务器...");
63
+ return new Promise((resolve, reject) => {
64
+ const server = new WebSocket(wssUrls[0]);
65
+ server.onopen = () => {
66
+ resolve(server);
67
+ };
68
+ server.onerror = err => {
69
+ reject(err);
70
+ };
71
+ }).then(server => {
72
+ console.log("连接websocket服务器成功!");
73
+ // 找到了可用websocket服务器端口
74
+ this.initWebsocketCallback(server);
75
+ console.log("尝试获取扫描设备列表...");
76
+ // 发送一个获取扫描设备列表的命令,询问本地计算机安装了多少个扫描设备的驱动程序
77
+ this.loadDevices();
78
+ return server;
79
+ }, err => {
80
+ // 如果连接失败,则尝试连接其他端口
81
+ if (wssUrls.length > 1) {
82
+ return this.getConnectedServer(wssUrls.slice(1));
83
+ }
84
+ throw err;
85
+ });
86
+ }
87
+
88
+ /**
89
+ * 尝试检测websocket哪个端口可以成功连接
90
+ */
91
+ tryConnect() {
92
+ // 以下一共定义了5个websocket端口,如果有端口被占用,则会自动尝试连接下一个端口
93
+ const wssUrls = ["ws://127.0.0.1:1001", "ws://127.0.0.1:2001", "ws://127.0.0.1:3001", "ws://127.0.0.1:4001", "ws://127.0.0.1:5001"];
94
+ this.getConnectedServer(wssUrls);
95
+ }
96
+
97
+ /**
98
+ * 初始化websocket相关的函数绑定
99
+ * @param {WebSocket} server - WebSocket服务器实例
100
+ */
101
+ initWebsocketCallback(server) {
102
+ this.h5socket = server;
103
+ this.h5socket.onerror = this.onSocketError.bind(this);
104
+ this.h5socket.onmessage = this.onSocketMessage.bind(this);
105
+ }
106
+
107
+ /**
108
+ * WebSocket错误处理
109
+ * @param {Event} event - 错误事件
110
+ */
111
+ onSocketError(event) {
112
+ alert("无法连接扫描服务程序,请检查扫描服务程序是否已经启动!");
113
+ console.log("WebSocket error: " + event.data);
114
+ }
115
+
116
+ /**
117
+ * 判断回调函数是否存在
118
+ * @param {Function} f - 要检查的函数
119
+ * @returns {boolean} 函数是否存在且为函数类型
120
+ */
121
+ isCallbackExist(f) {
122
+ if (!f || typeof f === "undefined" || f === undefined) {
123
+ return false;
124
+ }
125
+ return typeof f === "function";
126
+ }
127
+
128
+ /**
129
+ * WebSocket消息处理
130
+ * @param {MessageEvent} event - WebSocket消息事件
131
+ */
132
+ onSocketMessage(event) {
133
+ const msg = JSON.parse(event.data);
134
+ // console.log(msg);
135
+ switch (msg.cmd_type) {
136
+ case "getDevicesList":
137
+ {
138
+ // 获取设备信息列表返回结果
139
+ if (this.isCallbackExist(this.onGetDevicesListEvent)) {
140
+ this.onGetDevicesListEvent(msg);
141
+ }
142
+ break;
143
+ }
144
+ case "scanComplete":
145
+ {
146
+ // 扫描完成
147
+ this.imageCount = msg.imageCount;
148
+ if (this.isCallbackExist(this.onScanFinishedEvent)) {
149
+ this.onScanFinishedEvent(msg);
150
+ }
151
+ break;
152
+ }
153
+ case "selectScanDevice":
154
+ {
155
+ // 选择扫描设备结果
156
+ this.scaner_work_config.deviceIndex = msg.currentIndex; // 当前选中的设备索引
157
+ this.scaner_work_config.showDialog = msg.showDialog; // 是否显示设备内置对话框
158
+ this.scaner_work_config.autoFeedEnable = msg.autoFeedEnable; // 是否使用自动进纸器(需要设备支持才能正常工作)
159
+ this.scaner_work_config.autoFeed = msg.autoFeed; // 是否自动装填纸张(需要设备支持才能正常工作)
160
+ this.scaner_work_config.dupxMode = msg.dupxMode; // 是否使用双面扫描模式(需要设备支持才能正常工作)
161
+ this.scaner_work_config.autoDeskew = msg.autoDeskew; // 是否使用自动纠偏模式(需要设备支持才能正常工作)
162
+ this.scaner_work_config.autoBorderDetection = msg.autoBorderDetection; // 是否使用自动边框检测(需要设备支持才能正常工作)
163
+
164
+ if (this.isCallbackExist(this.onSelectScanDeviceEvent)) {
165
+ this.onSelectScanDeviceEvent(msg);
166
+ }
167
+ break;
168
+ }
169
+ case "getImageCount":
170
+ {
171
+ // 获取扫描图片数量
172
+ this.imageCount = msg.imageCount;
173
+ if (this.isCallbackExist(this.onGetImageCountEvent)) {
174
+ this.onGetImageCountEvent(msg);
175
+ }
176
+ break;
177
+ }
178
+ case "getAllImage":
179
+ {
180
+ // 获取所有图片
181
+ this.imageCount = msg.imageCount;
182
+ if (this.isCallbackExist(this.onGetAllImageEvent)) {
183
+ this.onGetAllImageEvent(msg);
184
+ }
185
+ break;
186
+ }
187
+ case "getImageById":
188
+ {
189
+ // 获取某一页图片的base64编码数据
190
+ this.imageCount = msg.imageCount;
191
+ if (this.isCallbackExist(this.onGetImageByIdEvent)) {
192
+ this.onGetImageByIdEvent(msg);
193
+ }
194
+ break;
195
+ }
196
+ case "loadImageFromUrl":
197
+ {
198
+ // 从URL加载图片
199
+ this.imageCount = msg.imageCount;
200
+ if (this.isCallbackExist(this.onLoadImageFromUrlEvent)) {
201
+ this.onLoadImageFromUrlEvent(msg);
202
+ }
203
+ break;
204
+ }
205
+ case "rotateImage":
206
+ {
207
+ // 旋转图片
208
+ this.imageCount = msg.imageCount;
209
+ if (this.isCallbackExist(this.onRotateImageEvent)) {
210
+ this.onRotateImageEvent(msg);
211
+ }
212
+ break;
213
+ }
214
+ case "getImageSize":
215
+ {
216
+ // 获取图片尺寸
217
+ this.imageCount = msg.imageCount;
218
+ if (this.isCallbackExist(this.onGetImageSizeEvent)) {
219
+ this.onGetImageSizeEvent(msg);
220
+ }
221
+ break;
222
+ }
223
+ case "uploadAllImageAsPdfToUrl":
224
+ {
225
+ // 上传pdf图像到指定的URL的回调
226
+ if (this.isCallbackExist(this.onUploadAllImageAsPdfToUrlEvent)) {
227
+ this.onUploadAllImageAsPdfToUrlEvent(msg);
228
+ }
229
+ break;
230
+ }
231
+ case "uploadAllImageAsTiffToUrl":
232
+ {
233
+ // 上传tiff图像到指定的URL的回调
234
+ if (this.isCallbackExist(this.onUploadAllImageAsTiffToUrlEvent)) {
235
+ this.onUploadAllImageAsTiffToUrlEvent(msg);
236
+ }
237
+ break;
238
+ }
239
+ case "uploadJpgImageByIndex":
240
+ {
241
+ // 上传jpg图像到指定的URL的回调
242
+ if (this.isCallbackExist(this.onUploadJpgImageByIndexEvent)) {
243
+ this.onUploadJpgImageByIndexEvent(msg);
244
+ }
245
+ break;
246
+ }
247
+ case "upload":
248
+ {
249
+ this.imageCount = msg.imageCount;
250
+ // 用户点击了界面里面的"开始上传"按钮的时间回调
251
+ if (this.isCallbackExist(this.onUploadEvent)) {
252
+ this.onUploadEvent(msg);
253
+ }
254
+ break;
255
+ }
256
+ case "imageEdited":
257
+ {
258
+ // 用户在扫描托盘程序里面对图片进行了编辑操作的回调
259
+ if (this.isCallbackExist(this.onImageEditedEvent)) {
260
+ this.onImageEditedEvent(msg);
261
+ }
262
+ break;
263
+ }
264
+ case "imageDrap":
265
+ {
266
+ // 用户在扫描托盘程序里面对图片进行了拖拽操作的回调
267
+ if (this.isCallbackExist(this.onImageDrapEvent)) {
268
+ this.onImageDrapEvent(msg);
269
+ }
270
+ break;
271
+ }
272
+ }
273
+ }
274
+
275
+ /**
276
+ * 通过websocket发送数据给webscoket服务端
277
+ * @param {Object} commandData - 要发送的命令数据
278
+ */
279
+ sendWebSocketCommand(commandData) {
280
+ try {
281
+ if (this.h5socket.readyState === 1) {
282
+ this.h5socket.send(JSON.stringify(commandData));
283
+ } else {
284
+ alert("发送扫描指令失败!请刷新页面或者检查托盘扫描程序是否已经正常运行!");
285
+ }
286
+ } catch (e) {
287
+ alert("发送扫描指令失败!" + e);
288
+ }
289
+ }
290
+
291
+ /**
292
+ * 设置授权信息
293
+ * @param {string} licenseMode - 授权模式
294
+ * @param {string} key1 - 授权密钥1
295
+ * @param {string} key2 - 授权密钥2
296
+ * @param {string} licenseServerUrl - 授权服务器URL
297
+ */
298
+ setLicenseKey(licenseMode, key1, key2, licenseServerUrl) {
299
+ const cmdObj = {
300
+ cmd_type: "setLicenseKey",
301
+ licenseMode: licenseMode,
302
+ key1: key1,
303
+ key2: key2,
304
+ url: licenseServerUrl
305
+ };
306
+ this.sendWebSocketCommand(cmdObj);
307
+ }
308
+
309
+ /**
310
+ * 加载所有可用的扫描设备
311
+ */
312
+ loadDevices() {
313
+ const cmdObj = {
314
+ cmd_type: "getDevicesList"
315
+ };
316
+ this.sendWebSocketCommand(cmdObj);
317
+ }
318
+
319
+ /**
320
+ * 设置当前选中的扫描设备id
321
+ * @param {number} deviceIndex - 设备索引
322
+ */
323
+ selectScanDevice(deviceIndex) {
324
+ const cmdObj = {
325
+ cmd_type: "selectScanDevice",
326
+ deviceIndex: deviceIndex
327
+ };
328
+ this.sendWebSocketCommand(cmdObj);
329
+ }
330
+
331
+ /**
332
+ * 开始扫描
333
+ */
334
+ startScan() {
335
+ const cmdObj = {
336
+ cmd_type: "startScan",
337
+ config: this.scaner_work_config
338
+ };
339
+ this.sendWebSocketCommand(cmdObj);
340
+ }
341
+
342
+ /**
343
+ * 清除全部扫描结果
344
+ */
345
+ clearAll() {
346
+ const cmdObj = {
347
+ cmd_type: "clearAll"
348
+ };
349
+ this.sendWebSocketCommand(cmdObj);
350
+ }
351
+
352
+ /**
353
+ * 获取图像总数
354
+ */
355
+ getImageCount() {
356
+ const cmdObj = {
357
+ cmd_type: "getImageCount"
358
+ };
359
+ this.sendWebSocketCommand(cmdObj);
360
+ }
361
+
362
+ /**
363
+ * 获取所有图像
364
+ */
365
+ getAllImage() {
366
+ const cmdObj = {
367
+ cmd_type: "getAllImage"
368
+ };
369
+ this.sendWebSocketCommand(cmdObj);
370
+ }
371
+
372
+ /**
373
+ * 发送指令获取某一页图像到托盘服务
374
+ * @param {number} index - 图像索引
375
+ */
376
+ getImageById(index) {
377
+ const cmdObj = {
378
+ cmd_type: "getImageById",
379
+ index: index
380
+ };
381
+ this.sendWebSocketCommand(cmdObj);
382
+ }
383
+
384
+ /**
385
+ * 发送指令远程加载服务器端的多页图像到托盘服务
386
+ * @param {string} url - 图像URL
387
+ */
388
+ loadImageFromUrl(url) {
389
+ const cmdObj = {
390
+ cmd_type: "loadImageFromUrl",
391
+ url: url
392
+ };
393
+ this.sendWebSocketCommand(cmdObj);
394
+ }
395
+
396
+ /**
397
+ * 发送指令旋转某一页图像到托盘服务
398
+ * @param {number} index - 图像索引
399
+ * @param {number} angle - 旋转角度
400
+ */
401
+ rotateImage(index, angle) {
402
+ const cmdObj = {
403
+ cmd_type: "rotateImage",
404
+ index: index,
405
+ angle: angle
406
+ };
407
+ this.sendWebSocketCommand(cmdObj);
408
+ }
409
+
410
+ /**
411
+ * 发送指令获取某一页图像的宽度到托盘服务
412
+ * @param {number} index - 图像索引
413
+ */
414
+ getImageSize(index) {
415
+ const cmdObj = {
416
+ cmd_type: "getImageSize",
417
+ index: index
418
+ };
419
+ this.sendWebSocketCommand(cmdObj);
420
+ }
421
+
422
+ /**
423
+ * 发送指令删除某一页图像到托盘服务
424
+ * @param {number} index - 图像索引
425
+ */
426
+ deleteImageByIndex(index) {
427
+ const cmdObj = {
428
+ cmd_type: "deleteImageByIndex",
429
+ index: index
430
+ };
431
+ this.sendWebSocketCommand(cmdObj);
432
+ }
433
+
434
+ /**
435
+ * 以pdf格式上传全部图像到服务器端
436
+ * @param {string} url - 上传URL
437
+ * @param {string} id - 标识ID
438
+ * @param {string} desc - 描述信息
439
+ */
440
+ uploadAllImageAsPdfToUrl(url, id, desc) {
441
+ const cmdObj = {
442
+ cmd_type: "uploadAllImageAsPdfToUrl",
443
+ url: url,
444
+ id: id,
445
+ desc: desc
446
+ };
447
+ this.sendWebSocketCommand(cmdObj);
448
+ }
449
+
450
+ /**
451
+ * 以tiff格式上传全部图像到服务器端
452
+ * @param {string} url - 上传URL
453
+ * @param {string} id - 标识ID
454
+ * @param {string} desc - 描述信息
455
+ */
456
+ uploadAllImageAsTiffToUrl(url, id, desc) {
457
+ const cmdObj = {
458
+ cmd_type: "uploadAllImageAsTiffToUrl",
459
+ url: url,
460
+ id: id,
461
+ desc: desc
462
+ };
463
+ this.sendWebSocketCommand(cmdObj);
464
+ }
465
+
466
+ /**
467
+ * 以jpg格式上传某一页图像到服务器端
468
+ * @param {string} url - 上传URL
469
+ * @param {string} id - 标识ID
470
+ * @param {string} desc - 描述信息
471
+ * @param {number} index - 图像索引
472
+ */
473
+ uploadJpgImageByIndex(url, id, desc, index) {
474
+ const cmdObj = {
475
+ cmd_type: "uploadJpgImageByIndex",
476
+ index: index,
477
+ url: url,
478
+ id: id,
479
+ desc: desc
480
+ };
481
+ this.sendWebSocketCommand(cmdObj);
482
+ }
483
+
484
+ /**
485
+ * 全部图像保存到客户端本地文件
486
+ * @param {string} filename - 文件名
487
+ */
488
+ saveAllImageToLocal(filename) {
489
+ const cmdObj = {
490
+ cmd_type: "saveAllImageToLocal",
491
+ filename: filename
492
+ };
493
+ this.sendWebSocketCommand(cmdObj);
494
+ }
495
+
496
+ /**
497
+ * 从客户端本地读取图像,通过打开文件对话框选择图像文件
498
+ */
499
+ openClientLocalfile() {
500
+ const cmdObj = {
501
+ cmd_type: "openClientLocalfile"
502
+ };
503
+ this.sendWebSocketCommand(cmdObj);
504
+ }
505
+
506
+ /**
507
+ * ftp上传全部图像文件到服务器端
508
+ * @param {string} serverIp - 服务器IP
509
+ * @param {number} port - 端口
510
+ * @param {string} username - 用户名
511
+ * @param {string} password - 密码
512
+ * @param {string} serverPath - 服务器路径
513
+ * @param {string} filename - 文件名
514
+ */
515
+ ftpUploadAllImage(serverIp, port, username, password, serverPath, filename) {
516
+ const cmdObj = {
517
+ cmd_type: "ftpUploadAllImage",
518
+ serverIp: serverIp,
519
+ port: port,
520
+ username: username,
521
+ password: password,
522
+ serverPath: serverPath,
523
+ filename: filename
524
+ };
525
+ this.sendWebSocketCommand(cmdObj);
526
+ }
527
+
528
+ /**
529
+ * 设置上传按钮是否可见
530
+ * @param {boolean} visible - 是否可见
531
+ */
532
+ setUploadButtonVisible(visible) {
533
+ const cmdObj = {
534
+ cmd_type: "setUploadButtonVisible",
535
+ visible: visible
536
+ };
537
+ this.sendWebSocketCommand(cmdObj);
538
+ }
539
+
540
+ /**
541
+ * 设置焦点
542
+ */
543
+ setFocus() {
544
+ const cmdObj = {
545
+ cmd_type: "focus"
546
+ };
547
+ this.sendWebSocketCommand(cmdObj);
548
+ }
549
+
550
+ /**
551
+ * 隐藏窗口
552
+ */
553
+ hidden() {
554
+ const cmdObj = {
555
+ cmd_type: "hidden"
556
+ };
557
+ this.sendWebSocketCommand(cmdObj);
558
+ }
559
+
560
+ /**
561
+ * 关闭websocket连接
562
+ */
563
+ closeWebSocket() {
564
+ this.h5socket.close();
565
+ }
566
+ }
567
+
568
+ // CommonJS 兼容性导出
569
+ if (typeof module !== "undefined" && module.exports) {
570
+ module.exports = ScanOnWeb;
571
+ }
572
+
573
+ // UMD 兼容性导出
574
+ if (typeof window !== "undefined") {
575
+ window.ScanOnWeb = ScanOnWeb;
576
+ }
577
+
578
+ export { ScanOnWeb as default };