网页扫描图像并以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的各个控件之前,引用头文件并必…...
TDengine 快速体验(Docker 镜像方式)
简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能,本节首先介绍如何通过 Docker 快速体验 TDengine,然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker,请使用 安装包的方式快…...
Xshell远程连接Kali(默认 | 私钥)Note版
前言:xshell远程连接,私钥连接和常规默认连接 任务一 开启ssh服务 service ssh status //查看ssh服务状态 service ssh start //开启ssh服务 update-rc.d ssh enable //开启自启动ssh服务 任务二 修改配置文件 vi /etc/ssh/ssh_config //第一…...
【Go】3、Go语言进阶与依赖管理
前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课,做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程,它的核心机制是 Goroutine 协程、Channel 通道,并基于CSP(Communicating Sequential Processes࿰…...
Linux-07 ubuntu 的 chrome 启动不了
文章目录 问题原因解决步骤一、卸载旧版chrome二、重新安装chorme三、启动不了,报错如下四、启动不了,解决如下 总结 问题原因 在应用中可以看到chrome,但是打不开(说明:原来的ubuntu系统出问题了,这个是备用的硬盘&a…...
实现弹窗随键盘上移居中
实现弹窗随键盘上移的核心思路 在Android中,可以通过监听键盘的显示和隐藏事件,动态调整弹窗的位置。关键点在于获取键盘高度,并计算剩余屏幕空间以重新定位弹窗。 // 在Activity或Fragment中设置键盘监听 val rootView findViewById<V…...
【论文阅读28】-CNN-BiLSTM-Attention-(2024)
本文把滑坡位移序列拆开、筛优质因子,再用 CNN-BiLSTM-Attention 来动态预测每个子序列,最后重构出总位移,预测效果超越传统模型。 文章目录 1 引言2 方法2.1 位移时间序列加性模型2.2 变分模态分解 (VMD) 具体步骤2.3.1 样本熵(S…...
Android 之 kotlin 语言学习笔记三(Kotlin-Java 互操作)
参考官方文档:https://developer.android.google.cn/kotlin/interop?hlzh-cn 一、Java(供 Kotlin 使用) 1、不得使用硬关键字 不要使用 Kotlin 的任何硬关键字作为方法的名称 或字段。允许使用 Kotlin 的软关键字、修饰符关键字和特殊标识…...
.Net Framework 4/C# 关键字(非常用,持续更新...)
一、is 关键字 is 关键字用于检查对象是否于给定类型兼容,如果兼容将返回 true,如果不兼容则返回 false,在进行类型转换前,可以先使用 is 关键字判断对象是否与指定类型兼容,如果兼容才进行转换,这样的转换是安全的。 例如有:首先创建一个字符串对象,然后将字符串对象隐…...
代码随想录刷题day30
1、零钱兑换II 给你一个整数数组 coins 表示不同面额的硬币,另给一个整数 amount 表示总金额。 请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑出总金额,返回 0 。 假设每一种面额的硬币有无限个。 题目数据保证结果符合 32 位带…...
Python Einops库:深度学习中的张量操作革命
Einops(爱因斯坦操作库)就像给张量操作戴上了一副"语义眼镜"——让你用人类能理解的方式告诉计算机如何操作多维数组。这个基于爱因斯坦求和约定的库,用类似自然语言的表达式替代了晦涩的API调用,彻底改变了深度学习工程…...
