网页扫描图像并以pdf格式上传到服务器端
本文描述如何通过网页驱动扫描仪、高拍仪等图像扫描设备进行图像扫描,扫描结果经编辑修改后以pdf压缩格式上传到后台java程序中进行服务器端落盘保存。
图像扫描上传如文字描述顺序所介绍,先要驱动扫描设备工作,进行纸张数据的光学扫描操作形成图像数据,之后对扫描结果进行剪切、旋转等编辑处理工作,最后的步骤是将加工处理好的结果上传到服务器端进行保存,这里面涉及到前端网页编程、后台服务器端编程等工作,以下按顺序介绍前后台如何具体是如何实现的。
一、准备工作
客户端计算机如果想进行图像扫描,首先需要连接硬件图像扫描设备,一般通过USB数据线与客户端电脑连接,当然如果局域网环境内有其他扫描仪设备,也可以使用局域网内的共享扫描设备,以上两种方式均需要安装扫描仪硬件设备对应的驱动程序,可以根据硬件设备的品牌型号到对应厂家下载驱动程序在客户端电脑安装配置。如果驱动程序有多种类型,选择包含twain驱动程序的下载使用。
二、设计交互网页界面
用户的电脑不一定只连接一台扫描设备,想让用户进行图像扫描时到底使用哪一台扫描设备需要有web交互界面供用户选择确定,另外在系统内有多个驱动程序的情况下windows并不能识别出来当前哪个设备处于可用状态,哪些设备已经断开与计算机的连接或者网络,所以需要由用户选择确认一下到底要使用哪个设备进行扫描,类似的,还有扫描时的分辨率DPI设置、扫描后图像是彩色还是灰度或黑白色的设置等等,这些都需要设计UI界面供用户设定。
这里给出读取系统连接的扫描仪并填充到下拉列表的关键核心代码示例:
<form>。。。<div class="block"><label for="devices">扫描设备:</label><select id="devices"></select></div><div class="block"><label for="dpi_x">设备输入分辨率:</label><input type="text" id="dpi_x" value="300" style="width: 25px;" /> X<input type="text" id="dpi_y" value="300" style="width: 25px;" /></div><div class="block"><label for="showDialog">是否显示内置对话框:</label><select id="showDialog"><option value="true">显示</option><option value="false" selected>不显示</option></select></div><div class="block"><label for="feedEnable">自动进纸模式:</label><select id="feedEnable"><option value="true">是</option><option value="false" selected>否</option></select></div><div class="block"><label for="autoFeed">自动装填纸张:</label><select id="autoFeed"><option value="true">是</option><option value="false" selected>否</option></select></div><div class="block"><label>双面模式:</label><select id="dupxMode"><option value="true">是</option><option value="false" selected>否</option></select></div><div class="block"><label>自动纠偏:</label><select id="autoDeskew"><option value="true">是</option><option value="false" selected>否</option></select></div><div class="block"><label>自动边框检测:</label><select id="autoBorderDetection"><option value="true">是</option><option value="false" selected>否</option></select></div>
。。。。</form><script src="./scanonweb.js" type="text/javascript"></script><script type="text/javascript">var scanonweb = new ScanOnWeb();//响应返回扫描设备列表的回调函数scanonweb.onGetDevicesListEvent = function (msg) {var deviceListDom = document.getElementById('devices');//clear devices listdeviceListDom.innerHTML = "";for (var i = 0; i < deviceListDom.childNodes.length; i++) {ardeviceListDomea.removeChild(deviceListDom.options[0]);deviceListDom.remove(0);deviceListDom.options[0] = null;}//add devices infofor (var i = 0; i < msg.devices.length; ++i) {var opt = document.createElement("option");opt.innerHTML = msg.devices[i];if (i == msg.currentIndex) {opt.selected = true;}deviceListDom.appendChild(opt);}}
上面的代码自动读取客户端计算机已经安装的扫描设备信息并自动填充到下拉列表框里面供用户选择使用。
三、驱动扫描设备进行图像扫描
前端网页添加“扫描”按钮,点击事件执行javascript代码去驱动扫描仪进行图像扫描,关键核心代码如下:
//开始扫描命令function startScan() {if (document.getElementById("devices").selectedIndex == -1) {alert('请先刷新或者选中要使用的扫描设备后再开始扫描!');return;}//以下获取界面中的扫描参数设定scanonweb.scaner_work_config.dpi_x = document.getElementById("dpi_x").value;scanonweb.scaner_work_config.dpi_y = document.getElementById("dpi_y").value;scanonweb.scaner_work_config.deviceIndex = document.getElementById("devices").selectedIndex;scanonweb.scaner_work_config.showDialog = document.getElementById("showDialog").value;scanonweb.scaner_work_config.autoFeedEnable = document.getElementById("feedEnable").value;scanonweb.scaner_work_config.autoFeed = document.getElementById("autoFeed").value;scanonweb.scaner_work_config.dupxMode = document.getElementById("dupxMode").value;scanonweb.scaner_work_config.autoDeskew = document.getElementById("autoDeskew").value;scanonweb.scaner_work_config.autoBorderDetection = document.getElementById("autoBorderDetection").value;//开始发送扫描指令scanonweb.startScan();}
此时用户在网页里面点击扫描按钮触发该函数执行后,扫描仪即可开始进行图像扫描,扫描结果可以通过scanonweb的托盘服务程序进行所见即所得的编辑处理,如:

上面的示例是演示了选中某个区域后进行打马赛克处理,选区内的内容已经变成了马赛克。
四、将图像处理结果上传到服务器端保存
扫描后的图像最终一般都会上传到服务器端进行保存,如果为了预览查看方便,可以使用pdf格式将扫描图像上传到服务器端后进行保存,以下是示例代码:
//按照pdf格式上传所有图像function uploadAllImageAsPdfFormat(){scanonweb.uploadAllImageAsPdfToUrl('http://localhost:8080/uploadDemo/fileUpload','1234','test'); }
服务器端接收的示例代码,以java为例:
package cn.brainysoft.uploaddemo;import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
import org.json.JSONObject;@WebServlet("/fileUpload")
@MultipartConfig
public class FileUploadServlet extends HttpServlet {private static final long serialVersionUID = 1L;private static final String SAVE_DIR = "uploadFiles";protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//获取字段数据String imageCount = request.getParameter("imageCount");String id = request.getParameter("id");String desc = request.getParameter("desc");//输出到控制台System.out.println("imageCount: " + imageCount);System.out.println("id: " + id);System.out.println("desc: " + desc);//获取文件数据Part filePart = request.getPart("image");String fileName = getFileName(filePart);InputStream fileContent = filePart.getInputStream();//保存文件到硬盘String appPath = request.getServletContext().getRealPath("");String savePath = appPath + File.separator + SAVE_DIR;File fileSaveDir = new File(savePath);if (!fileSaveDir.exists()) {fileSaveDir.mkdir();}String filePath = savePath + File.separator + fileName;FileOutputStream outStream = new FileOutputStream(filePath);byte[] buffer = new byte[4096];int bytesRead = -1;while ((bytesRead = fileContent.read(buffer)) != -1) {outStream.write(buffer, 0, bytesRead);}outStream.close();fileContent.close();System.out.println("文件保存路径:"+filePath);//构造返回的json数据Map<String, String> result = new HashMap<String, String>();result.put("url", request.getContextPath() + "/" + SAVE_DIR + "/" + fileName);JSONObject json = new JSONObject(result);String jsonString = json.toString();//设置返回的content type为jsonresponse.setContentType("application/json");//将json数据写入responseresponse.getWriter().write(jsonString);}//获取上传文件的文件名private String getFileName(final Part part) {final String partHeader = part.getHeader("content-disposition");for (String content : partHeader.split(";")) {if (content.trim().startsWith("filename")) {return content.substring(content.indexOf('=') + 1).trim().replace("\"", "");}}return null;}
}
上面的示例代码中将收到的pdf图像数据写入硬盘后返回给前端js后续浏览器请求查看时的url路径,具体存盘时的文件名怎么命名大家可以根据自己的需要去计算生成,一般前端收到服务器返回的pdf文件url路径后会通过ajax写在数据库里面保存,例如:
//响应上传pdf图像数据的回调函数scanonweb.onUploadAllImageAsPdfToUrlEvent = function (msg) {console.log("上传pdf图像数据到服务器端的响应结果:" + msg);if (msg.uploadResult) {//将msg.uploadResult转换为json对象var uploadResult = JSON.parse(msg.uploadResult);console.log("上传pdf图像数据保存到服务器端后的请求url地址:" + uploadResult.url);window.open("http://localhost:8080"+uploadResult.url);}}
以上示例较为零散,完整的前端示例代码可以从这里找到: https://www.brainysoft.cn/download/clientjs.zip
后台servlet接收代码完整工程压缩包地址:https://www.brainysoft.cn/download/servletUploadDemo.zip
相关文章:

网页扫描图像并以pdf格式上传到服务器端
本文描述如何通过网页驱动扫描仪、高拍仪等图像扫描设备进行图像扫描,扫描结果经编辑修改后以pdf压缩格式上传到后台java程序中进行服务器端落盘保存。图像扫描上传如文字描述顺序所介绍,先要驱动扫描设备工作,进行纸张数据的光学扫描操作形成…...
Airbyte入门
Airbyte 后端技术栈Java 17框架:JerseyAPI: OAS3数据库:PostgreSQL单元和E2E测试:JUnit 5编排:Temporal连接器技术栈连接器可以用任何语言编写。但是,最常见的语言是:Python3.9.0Java 17前端技术…...

研究人员在野外发现大量的信息窃取者 “Stealc “的样本
一个名为Stealc的新信息窃取者正在暗网上做广告,它可能成为其他同类恶意软件的一个值得竞争的对象。 "SEKOIA在周一的一份报告中说:"威胁行为者将Stealc作为一个功能齐全、随时可用的窃取者,其开发依赖于Vidar、Raccoon、Mars和Re…...

数据结构——复杂度讲解(2)
作者:几冬雪来 时间:2023年2月22日 内容:数据结构复杂度讲解 目录 前言: 复杂度讲解(2): 1.空间复杂度是什么: 2.空间复杂度讲解: 结尾: 前言&#x…...
【LeetCode】任务调度器 [M](贪心)
621. 任务调度器 - 力扣(LeetCode) 一、题目 给你一个用字符数组 tasks 表示的 CPU 需要执行的任务列表。其中每个字母表示一种不同种类的任务。任务可以以任意顺序执行,并且每个任务都可以在 1 个单位时间内执行完。在任何一个单位时间&…...

Spring代理模式——静态代理和动态代理
✅作者简介:2022年博客新星 第八。热爱国学的Java后端开发者,修心和技术同步精进。 🍎个人主页:Java Fans的博客 🍊个人信条:不迁怒,不贰过。小知识,大智慧。 💞当前专栏…...

Anaconda和PyCharm的一些安装问题和命令
今天更新了Windows上的Anaconda到2.3.2,PyCharm到2022.3。 ——发现是纯纯的犯贱orz。出了一堆问题。在这里记录一下供后来者参考。 Anaconda安装 将.\anaconda3\Scripts 和.\anaconda3\Library\bin添加到系统环境变量中。 新建环境的目录在.\anaconda3\envs下 N…...
sql优化建议
对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引。 应尽量避免在 where 子句中使用!或<>操作符,否则将引擎放弃使用索引而进行全表扫描。 应尽量避免在 where 子句中对字段进行 null 值判断&a…...

google hacker语句
哎,我就是沾边,就是不打实战( ̄o ̄) . z Z 文章目录前言一、什么是谷歌Docker?二、受欢迎的谷歌docker语句谷歌docker的例子日志文件易受攻击的 Web 服务器打开 FTP 服务器SSH私钥电子邮件列表实时摄像机MP3、电影和 PDF…...

Spring AOP
Spring AOP 文章目录Spring AOP1.概念1.面向切面编程2.AOP的目的3.AOP实现的分类4.AOP 术语2. Spring AOP的特性1.能力与目标2.AOP机制1.理解SpringAOP的代理2.AOP代理类的自调用代码的粒度如何让自调用走代理?3.Enabling AspectJ Support3. 定义切面与切点1. 声明切…...

【消费战略方法论】认识消费者的恒常原理(一):消费者稳态平衡原理
“消费战略”是塔望咨询基于大量的战略与营销实践经验结合心理学、经济学、传播学等相关专业学科的知识应用进行提炼与创造形成的战略方法体系。消费战略强调以消费者为导向,进行企业、品牌战略、品牌营销的制订和落地,企业经营的每个环节和输出的每个动…...

python居然能语音控制电脑壁纸切换,只需60行代码
前言 嗨喽~大家好呀,这里是魔王呐 ❤ ~! 家在日常的电脑使用中,都会有自己喜爱类型的桌面 单纯的桌面有时候会让人觉得单调 今天,就由我带领大家只用60行代码打造一款语音壁纸切换器程序, 让大家能够通过语音的方式来控制电脑去…...

内存泄露定位手段(c语言hook malloc相关方式)
如何确定有内存泄露问题,如何定位到内存泄露位置,如何写一个内存泄漏检测工具? 1:概述 内存泄露本质:其实就是申请调用malloc/new,但是释放调用free/delete有遗漏,或者重复释放的问题。 内存…...

STM32 CAN波特率计算
STM32 CAN波特率计算简介CAN总线收发,中断方式接收配置代码部分reference简介 CAN通信帧共分为数据帧、远程帧、错误帧、过载帧和帧间隔,本文这里以数据帧为例。 显性电平对应逻辑0,CAN_H和CAN_L之差为2.5V左右。而隐性电平对应逻辑1&#x…...
C/C++ 中#define 的妙用,让代码更美一些
C/C 中#define 的妙用,让代码更美一些 flyfish 1 数值类型输出易读的字符串形式 例如使用enum定义一些错误值,想要将数值类型的错误,输出易读的字符串形式 重要的一句代码 #define MAKE_PAIR(val) std::make_pair(val, #val)可以看到 #va…...

Linux文件系统操作与磁盘管理
查看磁盘和目录的容量 使用 df 命令查看磁盘的容量 df在实验楼的环境中你将看到如下的输出内容: 但在实际的物理主机上会更像这样: 物理主机上的 /dev/sda2 是对应着主机硬盘的分区,后面的数字表示分区号,数字前面的字母 a 表示…...

【Python】批量采集原神表情包~
嗨害大家好鸭~我是小熊猫(✿◡‿◡) 最近迷上了原神, 不自觉中就很喜欢保存广大旅行者制作的表情包~ 真的很有意思诶~ 源码资料电子书:点击此处跳转文末名片获取 一个个保存的话,好像效率很低嘛… 那我就发挥我小熊猫的老本行直接给把他们全部采集下…...
C语言基本语法注释类型关键字
C 基本语法 标识符 给变量所取的名字叫变量名,定义变量的名字需要遵循标识符的命名规则。 标识符是用来标识变量、符号常量、数组、函数、文件等名字的有效字符序列。 标识符的命名规则: 1.只能由字母、数字和下划线组成(例如:…...

【C ++】C++入门知识(二)
C入门(二) 作者:小卢 专栏:《C》 喜欢的话:世间因为少年的挺身而出,而更加瑰丽。 ——《人民日报》 1.引用 1.1.引用的概念及应用 引用(&) 引用不是新定义一个变量࿰…...

qt qchart学习
Qt Charts主要由QChartView、QChart、QLegend图例、坐标轴(由QAbstractAxis子类实现)、**数据源(由QAbstractSeries子类实现)**等组成使用QChart的前期准备1. Qt5.9及以上版本;2. .pro文件中添加QT charts3. 在使用QChart的各个控件之前,引用头文件并必…...

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)
题目:3442. 奇偶频次间的最大差值 I 思路 :哈希,时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况,哈希表这里用数组即可实现。 C版本: class Solution { public:int maxDifference(string s) {int a[26]…...

label-studio的使用教程(导入本地路径)
文章目录 1. 准备环境2. 脚本启动2.1 Windows2.2 Linux 3. 安装label-studio机器学习后端3.1 pip安装(推荐)3.2 GitHub仓库安装 4. 后端配置4.1 yolo环境4.2 引入后端模型4.3 修改脚本4.4 启动后端 5. 标注工程5.1 创建工程5.2 配置图片路径5.3 配置工程类型标签5.4 配置模型5.…...

Prompt Tuning、P-Tuning、Prefix Tuning的区别
一、Prompt Tuning、P-Tuning、Prefix Tuning的区别 1. Prompt Tuning(提示调优) 核心思想:固定预训练模型参数,仅学习额外的连续提示向量(通常是嵌入层的一部分)。实现方式:在输入文本前添加可训练的连续向量(软提示),模型只更新这些提示参数。优势:参数量少(仅提…...

23-Oracle 23 ai 区块链表(Blockchain Table)
小伙伴有没有在金融强合规的领域中遇见,必须要保持数据不可变,管理员都无法修改和留痕的要求。比如医疗的电子病历中,影像检查检验结果不可篡改行的,药品追溯过程中数据只可插入无法删除的特性需求;登录日志、修改日志…...
数据链路层的主要功能是什么
数据链路层(OSI模型第2层)的核心功能是在相邻网络节点(如交换机、主机)间提供可靠的数据帧传输服务,主要职责包括: 🔑 核心功能详解: 帧封装与解封装 封装: 将网络层下发…...

C++ Visual Studio 2017厂商给的源码没有.sln文件 易兆微芯片下载工具加开机动画下载。
1.先用Visual Studio 2017打开Yichip YC31xx loader.vcxproj,再用Visual Studio 2022打开。再保侟就有.sln文件了。 易兆微芯片下载工具加开机动画下载 ExtraDownloadFile1Info.\logo.bin|0|0|10D2000|0 MFC应用兼容CMD 在BOOL CYichipYC31xxloaderDlg::OnIni…...
.Net Framework 4/C# 关键字(非常用,持续更新...)
一、is 关键字 is 关键字用于检查对象是否于给定类型兼容,如果兼容将返回 true,如果不兼容则返回 false,在进行类型转换前,可以先使用 is 关键字判断对象是否与指定类型兼容,如果兼容才进行转换,这样的转换是安全的。 例如有:首先创建一个字符串对象,然后将字符串对象隐…...
【Go语言基础【13】】函数、闭包、方法
文章目录 零、概述一、函数基础1、函数基础概念2、参数传递机制3、返回值特性3.1. 多返回值3.2. 命名返回值3.3. 错误处理 二、函数类型与高阶函数1. 函数类型定义2. 高阶函数(函数作为参数、返回值) 三、匿名函数与闭包1. 匿名函数(Lambda函…...
IP如何挑?2025年海外专线IP如何购买?
你花了时间和预算买了IP,结果IP质量不佳,项目效率低下不说,还可能带来莫名的网络问题,是不是太闹心了?尤其是在面对海外专线IP时,到底怎么才能买到适合自己的呢?所以,挑IP绝对是个技…...
A2A JS SDK 完整教程:快速入门指南
目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库ÿ…...