当前位置: 首页 > news >正文

跨平台多用户环境下PDF表单“序列号生成的服务器端方案“

在PDF表单处理中,经常需要为每个表单生成唯一的序列号或表单编号。当所有表单都在同一台计算机上由同一用户处理时,可以通过JavaScript将编号存储在另一个表单或全局JavaScript数据中来实现。然而,当需要在多台计算机或多个用户环境中使用时,就需要更复杂的服务器端解决方案。

解决方案架构

要实现跨多环境的序列号生成功能,我们需要:

  1. 运行在表单中的JavaScript程序
  2. 运行在服务器上的实际Web服务

这里的"服务器"概念很广泛,可以是Windows Server、Mac OS Server、Linux等专业服务器系统,也可以是安装了服务器软件(如免费的XAMPP)的普通工作站。

技术限制

需要注意的是,Acrobat JavaScript实现的SOAP调用功能在免费Adobe Reader中仅在文档应用了"Forms"权限时才可用。要实现"Forms"权限,需要LiveCycle Reader Extensions软件。

表单设计

我们需要创建一个至少包含两个字段的表单:

  • 一个字段用于存储表单编号/序列号
  • 一个字段用于填写表单创建者或接收者的姓名

此外,表单还需要一个按钮来请求新的序列号。生成新序列号时,系统还应存储用户名和当前时间日期,以便后续追踪文档处理情况。

Web服务实现

为了简化PDF表单中的实现,我们首先创建提供唯一编号的Web服务。该服务应提供一个名为"getSerialNumber()"的函数,该函数接受一个参数(用户名)并返回包含新编号的字符串。

数据库设计

我们使用MySQL数据库来生成唯一且连续的编号。通过在MySQL数据库中定义一个作为主键的字段,每次插入新记录时,该索引会自动递增(从第一条记录的1开始)。

以下是创建所需数据库和表的MySQL命令:

CREATE DATABASE serialnumbers;
CREATE TABLE serialnumbers (idx INT(6) UNSIGNED AUTO_INCREMENT PRIMARY KEY, username VARCHAR(30), date TIMESTAMP);
CREATE USER 'newuser'@'localhost' IDENTIFIED BY 'password';
GRANT INSERT,SELECT ON serialnumbers to 'theUser'@'localhost';

PHP实现

以下是实现该系统的PHP脚本,它将用户名和当前日期写入数据库,然后返回新记录的索引:

<?phpfunction getNextSerial($userName) {$mysqli = new mysqli("localhost", "theUser", "thePassword", "serialnumbers");if (mysqli_connect_errno()) {printf("Connect failed: %s\n", mysqli_connect_error());exit();}$query = "INSERT INTO serialnumbers (username, date) VALUES (\"" . $mysqli->real_escape_string($userName) . "\", NOW())";$mysqli->query($query);$idx = $mysqli->insert_id;$mysqli->close();return $idx;}ini_set("soap.wsdl_cache_enabled", "0");class getSerialResponse{public $return;}class getSerialClass{public function getSerialNumber($parameters){$response = new getSerialResponse();$response->return = getNextSerial($parameters->userName);return $response;}}   $server = new SoapServer("GetSerialNumber.wsdl");$server->setClass("getSerialClass");$server->handle();
?>

此脚本假设在同一目录中存在名为"GetSerialNumber.wsdl"的WSDL文件,该文件又引用也需要位于同一目录中的模式文件。

PDF表单中的JavaScript实现

在示例文档中,我们使用以下JavaScript代码:

// 获取WSDL代理对象
var myProxy = Net.SOAP.connect("http://localhost/GetSerial/GetSerialNumber.wsdl?wsdl");// 从字段获取用户名
var userName = this.getField("UserName").value;
if (userName != "") {var result = myProxy.getSerialNumber(userName);this.getField("Result").value = util.printf("%04d", Number(result));// 隐藏按钮event.target.display = display.hidden;
}
else {app.alert("Please fill in a user name");
}

代码首先加载与PHP Web服务一起安装的WSDL文件,然后获取"UserName"字段的内容。如果该字段非空,则通过SOAP代理对象请求新的序列号,将结果格式化为带前导零的数字,并隐藏用于生成序列号的按钮。

部署注意事项

使用这些文件时,必须确保调整JavaScript和PHP代码中使用的所有URL,使其与您的安装匹配。示例中使用的是"http://localhost/GetSerial",您需要搜索该字符串并将其替换为安装目录的正确路径。

替代方案

如果SOAP实现过于复杂,可以考虑基于FDF的解决方案,它也能实现数据库与PDF表单之间的数据交互,而无需使用SOAP。

系统架构图

PDF表单 SOAP服务 MySQL数据库 调用getSerialNumber(用户名) INSERT记录(用户名,时间) 返回自增ID 返回格式化序列号 PDF表单 SOAP服务 MySQL数据库

修正后的PHP Web服务代码

<?php
// 错误报告设置(开发环境)
error_reporting(E_ALL);
ini_set('display_errors', 1);/*** 获取下一个序列号* @param string $userName 用户名(自动过滤SQL注入)* @return int 序列号* @throws Exception 数据库错误时抛出*/
function getNextSerial($userName) {// 数据库配置(应存储在配置文件中)$config = ['host' => 'localhost','user' => 'serial_user','pass' => 'secure_password_123','db'   => 'serial_numbers_db','port' => 3306];// 创建安全的数据库连接$mysqli = new mysqli($config['host'],$config['user'],$config['pass'],$config['db'],$config['port']);// 检查连接if ($mysqli->connect_errno) {throw new Exception("数据库连接失败: " . $mysqli->connect_error);}// 准备预处理语句防止SQL注入$stmt = $mysqli->prepare("INSERT INTO serial_numbers (username, create_date) VALUES (?, NOW())");if (!$stmt) {throw new Exception("预处理失败: " . $mysqli->error);}// 绑定参数并执行$stmt->bind_param("s", $userName);if (!$stmt->execute()) {throw new Exception("执行失败: " . $stmt->error);}$serial = $mysqli->insert_id;// 清理资源$stmt->close();$mysqli->close();return $serial;
}// 禁用WSDL缓存(开发环境)
ini_set("soap.wsdl_cache_enabled", "0");try {// 创建SOAP服务$server = new SoapServer("SerialService.wsdl");// 注册服务类$server->setClass("SerialService");// 处理请求$server->handle();
} catch (Exception $e) {// 记录错误日志file_put_contents('soap_error.log', date('[Y-m-d H:i:s] ') . $e->getMessage() . PHP_EOL, FILE_APPEND);// 返回SOAP错误header("HTTP/1.1 500 Internal Server Error");die($e->getMessage());
}/*** SOAP服务类*/
class SerialService {/*** 获取序列号* @param string $userName 用户名* @return string 格式化后的序列号(如"000123")*/public function getSerialNumber($userName) {// 验证输入if (empty($userName) {throw new SoapFault("Client", "用户名不能为空");}if (strlen($userName) > 30) {throw new SoapFault("Client", "用户名最长30个字符");}try {$serial = getNextSerial($userName);return str_pad($serial, 6, '0', STR_PAD_LEFT);} catch (Exception $e) {throw new SoapFault("Server", $e->getMessage());}}
}
?>

修正后的JavaScript代码

/*** 生成序列号按钮点击事件*/
function generateSerialNumber() {try {// 1. 获取用户名输入var userNameField = this.getField("UserName");var userName = userNameField.value.trim();// 2. 验证输入if (!userName) {app.alert({cTitle: "输入错误",cMsg: "请输入用户名后再生成序列号",nIcon: 2 // 警告图标});return;}// 3. 连接SOAP服务var wsdlUrl = "http://your-domain.com/SerialService.php?wsdl";var soapClient = Net.SOAP.connect(wsdlUrl);// 4. 调用服务var serialNumber = soapClient.getSerialNumber(userName);// 5. 显示结果this.getField("SerialNumber").value = serialNumber;this.getField("GenerateBtn").display = display.hidden;// 6. 记录生成时间this.getField("GenerationTime").value = util.printd("yyyy-mm-dd HH:MM:ss", new Date());} catch (e) {console.println("错误: " + e);app.alert({cTitle: "系统错误",cMsg: "生成序列号失败: " + e.message,nIcon: 3 // 错误图标});}
}// 初始化时检查是否已有序列号
if (this.getField("SerialNumber").value) {this.getField("GenerateBtn").display = display.hidden;
}

数据库优化方案

-- 创建专用数据库
CREATE DATABASE serial_numbers_db 
DEFAULT CHARACTER SET utf8mb4 
COLLATE utf8mb4_unicode_ci;-- 创建数据表(优化版)
CREATE TABLE serial_numbers (id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,username VARCHAR(30) NOT NULL,create_date DATETIME NOT NULL,client_ip VARCHAR(45),user_agent VARCHAR(255),INDEX idx_username (username),INDEX idx_date (create_date)
) ENGINE=InnoDB;-- 创建专用用户(最小权限原则)
CREATE USER 'serial_user'@'%' IDENTIFIED BY 'secure_password_123';
GRANT INSERT, SELECT ON serial_numbers_db.* TO 'serial_user'@'%';
FLUSH PRIVILEGES;

常见错误解决方案

错误类型可能原因解决方案
连接超时服务器未启动/防火墙阻止1. 检查PHP服务是否运行
2. 开放端口(通常80/443)
数据库错误凭证不正确/表不存在1. 验证数据库配置
2. 执行提供的SQL创建表
SOAP解析失败WSDL文件错误1. 确保WSDL可访问
2. 使用SoapUI测试服务
权限不足Adobe Reader限制1. 使用Acrobat Pro
2. 申请Reader扩展权限

部署检查清单

  1. 服务器环境

    • 安装PHP SOAP扩展 (php-soap)
    • 配置MySQL/MariaDB
    • 设置正确的文件权限
  2. PDF表单配置

    • 使用Acrobat Pro XI或更高版本
    • 启用JavaScript特权
    • 测试跨域访问
  3. 安全措施

    • 替换示例数据库密码
    • 配置HTTPS加密
    • 实施IP访问限制

如需进一步测试,可以使用以下SOAP请求示例(通过Postman):

POST /SerialService.php HTTP/1.1
Host: your-domain.com
Content-Type: text/xml;charset=UTF-8
SOAPAction: "urn:SerialService#getSerialNumber"<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ser="urn:SerialService"><soapenv:Header/><soapenv:Body><ser:getSerialNumber><userName>测试用户</userName></ser:getSerialNumber></soapenv:Body>
</soapenv:Envelope>

应用场景示例:

以下代码实现了 PDF文档与远程SOAP服务交互 的功能,核心应用场景包括:

  1. 在PDF中动态获取网络时间(互联网授时)
  2. 通过SOAP协议调用远程Web服务
  3. 将服务返回结果嵌入PDF表单字段
  4. 实现动态文档内容更新(如序列号生成、时间戳)

适用于:电子合同签署时间认证、动态表单数据填充、许可证激活系统等场景。


// 获取网络时间服务
try {const cURL = "http://quan.suning.com/getSysTime.do";const oRequest = {"ns1:getSysTime": { xmlns: "http://quan.suning.com/",inputString: "sysTime1"}};const response = Net.SOAP.request({cURL: cURL,oRequest: oRequest,cAction: "http://quan.suning.com/getSysTime",cVersion: SOAPVersion.version_1_2,bEncoded: true // 启用XML编码});app.alert("当前网络时间:" + response.sysTime2);
} catch (e) {console.println("SOAP请求错误: " + e.message);app.alert("时间同步失败,请检查网络连接");
}

使用
使用
发送请求
生成
调用服务
PDF_Application
+Net.HTTP
+Net.SOAP
+app.alert()
+util.printf()
SOAP_Client
+wireDump
+request()
+connect()
HTTP_Client
+request()
Time_Server
+getSysTime.do
+返回JSON/XML
WSDL_Proxy
+getSerialNumber()

  1. SOAP版本兼容性

    // 强制使用1.2版本
    cVersion: SOAPVersion.version_1_2
    
  2. 调试配置

    SOAP.wireDump = "true"; // 显示原始通信数据
    
  3. 跨域安全配置

    <!-- 服务器需配置CORS -->
    Access-Control-Allow-Origin: *
    Access-Control-Allow-Methods: POST
    
  4. PDF权限清单

    /* 需要启用的权限:- 网络访问- 表单提交- 动态内容执行- 外部对象访问
    */
    

常见错误解决方案
错误类型解决方案
SOAPError:检查XML命名空间与服务器端匹配
MethodNotAllowed确保服务器允许POST方法
权限拒绝启用Adobe Reader扩展权限
空响应验证SOAP Action URI正确性
编码错误设置bEncoded: true并检查XML结构

通过以上配置,可实现PDF文档与SOAP服务的稳定交互。建议使用Acrobat Pro XI以上版本进行调试,并始终在try-catch块中处理网络请求。

http://www.xdnf.cn/news/512857.html

相关文章:

  • 如何实现RTSP和RTMP低至100-200ms的延迟:直播SDK的技术突破
  • Metasploit框架与网络安全攻防技术解析
  • 标准库、HAl库和LL库(PC13初始化)
  • 【甲方安全建设】Python 项目静态扫描工具 Bandit 安装使用详细教程
  • 视差场(disparity field)
  • Linux之基础IO
  • MySQL 数据库备份与还原
  • iOS APP启动页及广告页的实现
  • 赋予AI更强的“思考”能力
  • 动态规划(4)可视化理解:图形化思考
  • Tomcat简述介绍
  • 10.8 LangChain三大模块深度实战:从模型交互到企业级Agent工具链全解析
  • 企业级小程序APP用户数据查询系统安全脆弱性分析及纵深防御体系构建
  • JUC入门(二)
  • [创业之路-362]:企业战略管理案例分析-3-战略制定-华为使命、愿景、价值观的演变过程
  • 开源项目实战学习之YOLO11:12.5 ultralytics-models-sam.py通用图像分割模型源码分析
  • Django学习
  • **HTTP/HTTPS基础** - URL结构(协议、域名、端口、路径、参数、锚点) - 请求方法(GET、POST) - 请求头/响应头 - 状态码含义
  • IS-IS 中间系统到中间系统
  • ASCII码表
  • 离散文本表示
  • Java IO框架
  • YOLO12改进-模块-引入Channel Reduction Attention (CRA)模块 降低模型复杂度,提升复杂场景下的目标定位与分类精度
  • 云原生安全:IaaS安全全解析(从基础到实践)
  • Linux 安装 Unreal Engine
  • 4.1.8文件共享
  • MCP实战:在扣子空间用扣子工作流MCP,一句话生成儿童故事rap视频
  • java中的Servlet3.x详解
  • 07、基础入门-SpringBoot-自动配置特性
  • wsl2中Ubuntu22.04配置静态IP地址