使用php基础方式实现word中表单处理

<?php/*** zipFile 类用于处理 .docx 文件的解压、修改和重新打包*/
class zipFile
{/** @var ZipArchive ZIP 文件对象 */private $zipFile;/** @var string 临时目录路径 */private $tempDir;/** @var string 嵌入的 Excel 文件临时目录路径 */private $excelTempDir;/** @var string 原始 .docx 文件路径 */private $docxPath;/*** 构造函数* * @param string $docxPath .docx 文件路径* @throws Exception 如果无法打开 .docx 文件*/public function __construct($docxPath){$this->docxPath = $docxPath;$this->zipFile = new ZipArchive();if ($this->zipFile->open($docxPath) !== TRUE) {throw new Exception("无法打开 .docx 文件: $docxPath");}$this->tempDir = sys_get_temp_dir() . '/docx_' . uniqid();mkdir($this->tempDir, 0777, true);// ✅ 正确命名:用于存放解压的 Excel 内容$this->excelTempDir = $this->tempDir . '/embedded_excel';}/*** 解压整个 .docx 到临时目录* 并自动解压其中的 Workbook1.xlsx(如果存在)*/public function extract(){// 1. 解压 .docx 主文件$this->zipFile->extractTo($this->tempDir);$this->zipFile->close();// echo "✅ .docx 已解压到: {$this->tempDir}\n";// 2. 查找并解压嵌入的 Excel 文件$embeddedXlsxPath = $this->tempDir . '/word/embeddings/Workbook1.xlsx';if (!file_exists($embeddedXlsxPath)) {echo "⚠️ 未找到嵌入的 Workbook1.xlsx\n";return;}$excelZip = new ZipArchive();if ($excelZip->open($embeddedXlsxPath) !== TRUE) {throw new Exception("无法打开嵌入的 Workbook1.xlsx");}// 创建目录并解压 Excel 内容mkdir($this->excelTempDir, 0777, true);$excelZip->extractTo($this->excelTempDir);$excelZip->close();echo "✅ Workbook1.xlsx 已解压到: {$this->excelTempDir}\n";}/*** 获取解压后的 sheet1.xml 路径* * @return string*/public function getSheet1Path(){$path = $this->excelTempDir . '/xl/worksheets/sheet1.xml';if (!file_exists($path)) {throw new Exception("未找到 sheet1.xml: $path");}return $path;}/*** 获取 sharedStrings.xml 路径(用于字符串修改)* * @return string*/public function getSharedStringsPath(){$path = $this->excelTempDir . '/xl/sharedStrings.xml';if (!file_exists($path)) {throw new Exception("未找到 sharedStrings.xml");}return $path;}/*** 获取文件内容(.docx 内任意文件)* * @param string $path 文件路径(相对于解压目录)* @return string|null*/public function getFileContent($path){$fullPath = $this->tempDir . '/' . ltrim($path, '/');return file_exists($fullPath) ? file_get_contents($fullPath) : null;}/*** 写入修改后的文件* * @param string $path 文件路径(相对于解压目录)* @param string $content 文件内容*/public function putFileContent($path, $content){$fullPath = $this->tempDir . '/' . ltrim($path, '/');$dir = dirname($fullPath);if (!is_dir($dir)) {mkdir($dir, 0777, true);}file_put_contents($fullPath, $content);}/*** 修改 Excel 单元格值(仅限数字)* * @param string $cell 单元格地址,如 'B2'* @param mixed $newValue 新值*/public function modifyExcelCell($cell, $newValue){// 1. 修改 Excel 数据 (sheet1.xml)$sheetFile = $this->getSheet1Path();// 使用 DOMDocument 替代 simplexml_load_file$dom = new DOMDocument();$dom->load($sheetFile);if (!$dom) {throw new Exception("无法加载 sheet1.xml");}$xpath = new DOMXPath($dom);$xpath->registerNamespace('x', 'http://schemas.openxmlformats.org/spreadsheetml/2006/main');$nodes = $xpath->query("//x:c[@r='$cell']");if ($nodes->length == 0) {throw new Exception("未找到单元格: $cell");}$cNode = $nodes->item(0);$vNodes = $xpath->query(".//x:v", $cNode);if ($vNodes->length == 0) {throw new Exception("单元格 $cell 缺少 <v> 节点");}$vNode = $vNodes->item(0);$oldValue = $vNode->nodeValue;$vNode->nodeValue = $newValue;$dom->save($sheetFile);echo "✅ 已修改 Excel 单元格 $cell: $oldValue → $newValue\n";// 2. 同步更新图表缓存$this->updateChartCache($cell, $newValue);}//批量更新public function updateDataAndChart($dataMap){foreach ($dataMap as $cell => $value) {$this->modifyExcelCell($cell, $value);}}/*** 更新 chart1.xml 中的缓存值(用于柱状图、折线图等)* * @param string $cell 单元格地址,如 'B2'* @param mixed  $newValue 新值* @param string $chartXmlPath 图表文件路径,默认为 chart1.xml*//*** 根据单元格地址,在 chart1.xml 中找到对应的 <c:f> 并更新其 numCache* * @param string $cell 单元格地址,如 'B2'* @param mixed  $newValue 新值*/private function updateChartCache($cell, $newValue){$chartPath = $this->tempDir . '/word/charts/chart1.xml';if (!file_exists($chartPath)) {echo "⚠️ 未找到 chart1.xml,跳过图表缓存更新\n";return;}// 使用 DOMDocument 替代 simplexml_load_file$dom = new DOMDocument();$dom->load($chartPath);if (!$dom) {throw new Exception("无法加载 chart1.xml");}// 注册命名空间$xpath = new DOMXPath($dom);$xpath->registerNamespace('c', 'http://schemas.openxmlformats.org/drawingml/2006/chart');$xpath->registerNamespace('a', 'http://schemas.openxmlformats.org/drawingml/2006/main');// 构造目标引用,如:Sheet1!$B$2$escapedCell = preg_replace('/([A-Z]+)/', '$1$', $cell); // B2 -> B$2$targetRef = "Sheet1!\$$escapedCell"; // 注意:Sheet1!$B$2// 查找 <c:f> 内容为 Sheet1!$B$2 的节点$fNodes = $xpath->query("//c:f[text()='$targetRef']");if ($fNodes->length == 0) {echo "🔍 未在 chart1.xml 中找到引用 $targetRef\n";return;}foreach ($fNodes as $fNode) {// 找到父级 <c:ser> 或 <c:pt> 等结构$parent = $fNode->parentNode;// 查找对应的 <c:numCache>$numCacheNodes = $xpath->query(".//c:numCache", $parent);if ($numCacheNodes->length == 0) {// echo "⚠️ 找到引用 $targetRef,但无 <c:numCache> 缓存\n";continue;}$numCache = $numCacheNodes->item(0);// 查找 <c:pt idx="0"> 下的 <c:v>$ptNodes = $xpath->query(".//c:pt[@idx='0']/c:v", $numCache);if ($ptNodes->length > 0) {$ptNode = $ptNodes->item(0);$oldValue = $ptNode->nodeValue;$ptNode->nodeValue = $newValue;echo "📊 已更新图表缓存 [$targetRef]: $oldValue → $newValue\n";} else {// 如果没有 pt,尝试创建(高级功能,可选)echo "⚠️ 未找到 <c:pt> 节点,无法更新缓存: $targetRef\n";}}// 保存修改$dom->save($chartPath);}/*** 重新打包为 .docx* * @param string $outputPath 输出文件路径*/public function pack($outputPath){// 1. 先打包嵌入的 Excel,并自动清理临时目录$this->repackEmbeddedWorkbook();// 2. 再打包整个 .docx 文件$newZip = new ZipArchive();if ($newZip->open($outputPath, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== TRUE) {throw new Exception("无法创建输出文件: $outputPath");}$files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($this->tempDir, FilesystemIterator::SKIP_DOTS),RecursiveIteratorIterator::LEAVES_ONLY);foreach ($files as $file) {if (!$file->isDir()) {$filePath = $file->getRealPath();$relativePath = substr($filePath, strlen($this->tempDir) + 1);$newZip->addFile($filePath, $relativePath);}}$newZip->close();echo "✅ 已打包为: $outputPath\n";}/*** 将修改后的 embedded_excel/ 目录重新打包为 Workbook1.xlsx* 并删除临时解压目录(确保中间文件不残留)*/private function repackEmbeddedWorkbook(){$embeddedXlsxPath = $this->tempDir . '/word/embeddings/Workbook1.xlsx';// 如果没有 embedded_excel 目录,说明没有 Excel 或未解压if (!is_dir($this->excelTempDir)) {echo "⚠️ 无嵌入 Excel 临时目录,跳过打包 Workbook1.xlsx\n";return;}// 删除旧的 Workbook1.xlsx(如果存在)if (file_exists($embeddedXlsxPath)) {unlink($embeddedXlsxPath);}// 创建新的 Zip 存档$zip = new ZipArchive();if ($zip->open($embeddedXlsxPath, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== TRUE) {throw new Exception("无法创建 Workbook1.xlsx");}$files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($this->excelTempDir, FilesystemIterator::SKIP_DOTS),RecursiveIteratorIterator::LEAVES_ONLY);foreach ($files as $file) {if (!$file->isDir()) {$filePath = $file->getRealPath();$relativePath = substr($filePath, strlen($this->excelTempDir) + 1);$zip->addFile($filePath, $relativePath);}}$zip->close();echo "✅ Workbook1.xlsx 已重新打包\n";// 🔥 关键修复:立即删除 embedded_excel 临时目录$this->rrmdir($this->excelTempDir);echo "🗑️  已清理 Excel 临时目录: {$this->excelTempDir}\n";}/*** 列出解压目录中的所有文件* * @return array 文件路径列表*/public function listFiles(){$files = [];if (!is_dir($this->tempDir)) return $files;$iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($this->tempDir, FilesystemIterator::SKIP_DOTS));foreach ($iterator as $file) {if ($file->isFile()) {$relativePath = substr($file->getPathname(), strlen($this->tempDir) + 1);$files[] = $relativePath;}}return $files;}/*** 清理临时文件*/public function cleanup(){if (is_dir($this->tempDir)) {$this->rrmdir($this->tempDir);}}/*** 递归删除目录* * @param string $dir 目录路径*/private function rrmdir($dir){$files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir, FilesystemIterator::SKIP_DOTS),RecursiveIteratorIterator::CHILD_FIRST);foreach ($files as $fileinfo) {$todo = ($fileinfo->isDir() ? 'rmdir' : 'unlink');$todo($fileinfo->getRealPath());}rmdir($dir);}
}// ✅ 使用示例(修复后)
try {$processor = new zipFile('表单.docx');$processor->extract();// ✅ 方法一:使用封装好的 modifyExcelCell$processor->modifyExcelCell('B2', 500);$processor->pack('output.docx');$processor->cleanup();
} catch (Exception $e) {echo "❌ 错误: " . $e->getMessage() . "\n";
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/917307.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/917307.shtml
英文地址,请注明出处:http://en.pswp.cn/news/917307.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Node.js 操作 MongoDB

目录 Node.js 操作 MongoDB 一、什么是 MongoDB&#xff1f; 二、MongoDB 的功能概览 三、MongoDB 的安装与启动 安装 MongoDB&#xff08;以本地安装为例&#xff09; 启动 MongoDB 四、Node.js 如何连接 MongoDB&#xff1f; 使用 Mongoose ODM 工具 建立连接 五、…

先学Python还是c++?

选择先学Python还是C&#xff0c;取决于你的学习目标、应用场景和职业规划。以下是两者的对比分析和建议&#xff0c;帮助你做出更适合自己的选择&#xff1a;一、核心差异对比维度PythonC学习曲线简单易上手&#xff08;语法接近自然语言&#xff09;复杂&#xff08;需理解指…

Trae + Notion MCP:将你的Notion数据库升级为智能对话机器人

前言 Notion作为一款功能强大的信息管理工具&#xff0c;被广泛用于项目跟踪、知识库构建和数据整理。然而&#xff0c;随着数据量的增长&#xff0c;我们常常会发现自己陷入了重复和繁琐的操作中。比如&#xff0c;为了找到符合特定条件的几条数据&#xff0c;需要在庞大的数…

【iOS】retain/release底层实现原理

文章目录前言前情知识retain和release的实现原理&#xff08;MRC手动管理&#xff09;retain&#xff08;MRC手动管理&#xff09;retain源码内联函数rootRetain源码相关的sidetable_tryRetain()方法retain底层工作流程总结releaserelease源码内联函数rootRelease源码小结前言 …

文件同步神器-rsync命令讲解

rsync 是一个强大的文件同步与传输工具&#xff0c;广泛用于本地或远程服务器之间的高效文件备份、镜像或同步。其核心优势是通过增量传输​&#xff08;仅传输文件差异部分&#xff09;和压缩减少数据传输量&#xff0c;同时支持保留文件元数据&#xff08;如权限、时间戳、所…

Rust: 工具链版本更新

遇到 cargo build --release 错误&#xff0c;比如&#xff0c;当前 Rust 工具链版本&#xff08;1.78.0&#xff09;低于依赖项所需的最低版本&#xff08;部分依赖要求 ≥1.82.0&#xff09;。以下是系统化的解决方案&#xff1a; &#x1f527; 一、升级 Rust 工具链&#x…

Prompt-to-Prompt| 修改Attention会有“反向传播”或梯度计算?

需要注意的几个问题&#xff1a;额外计算开销&#xff1a;Cross-Attention Control原因&#xff1a;Prompt-to-Prompt的编辑方法需要动态干预交叉注意力&#xff08;Cross-Attention&#xff09;层的权重&#xff0c;这会引入额外的计算和显存占用&#xff1a;需要缓存注意力矩…

电商API接口的优势、数据采集方法及功能说明

一、电商API接口的核心优势1. 高效性与准确性数据采集效率&#xff1a;API通过标准化参数&#xff08;如商品ID、类目&#xff09;直接获取结构化数据&#xff08;JSON/XML&#xff09;&#xff0c;无需解析HTML&#xff0c;减少误差。例如&#xff0c;采集1000条商品信息&…

iOS企业签名掉签,iOS企业签名掉签了怎么办?

不能上架到App Store的iOS应用 &#xff0c;几乎每一个开发者的选择都是通过iOS签名这种内测渠道来完成APP的上架任务&#xff0c;最常用的就是企业签名、超级签名以及TF上架&#xff0c;其中最受欢迎的当属于企业签名了。不过企业签名会出现掉签的现象&#xff0c;那么企业签名…

存储成本深度优化:冷热分层与生命周期管理——从视频平台年省200万实践解析智能存储架构

一、冷热分层&#xff1a;存储成本优化的核心逻辑1.1 数据访问的“二八定律”据行业统计&#xff0c;80%的访问集中在20%的热数据上&#xff0c;而超过90天的历史数据访问频率下降70%以上。某视频平台存储超10PB媒体文件&#xff0c;未分层前年存储成本高达680万元&#xff0c;…

Java设计模式之《备忘录模式》

目录 1. 概念 1.1、定义 1.2、适用场景 2、角色划分 3、实现 1、Originator&#xff08;发起人&#xff09; 2、Memento&#xff08;备忘录&#xff09; 3、Caretaker&#xff08;管理者&#xff09; 4、使用示例 4、优缺点 4.1、优点 4.2、缺点 前言 备忘录模式是…

SpringBoot 多环境配置

在实际项目开发中&#xff0c;不同环境往往有不同的配置需求&#xff1a; 开发环境&#xff08;dev&#xff09;&#xff1a;本地调试&#xff0c;连接测试数据库&#xff1b;测试环境&#xff08;test&#xff09;&#xff1a;接口联调&#xff0c;接近真实场景&#xff1b;生…

延凡智慧医院数字孪生平台

延凡智慧医院数字孪生平台是延凡科技依托物联网、数字孪生、AI 算法及边缘计算技术打造的医疗场景全要素数字化解决方案&#xff0c;通过构建医院物理实体与虚拟空间的实时映射&#xff0c;实现医疗资源优化、运营效率提升及患者体验升级。一、平台价值&#xff08;一&#xff…

谈谈WebAssembly、PWA、Web Workers的作用和场景

WebAssembly、PWA 和 Web Workers 是现代 Web 开发中提升性能、扩展能力的重要技术&#xff0c;各自解决不同场景的问题&#xff0c;以下结合实际使用经验分析&#xff1a;一、WebAssembly&#xff08;Wasm&#xff09;&#xff1a;高性能代码执行作用&#xff1a;WebAssembly …

嵌入式第十八课!!数据结构篇入门及单向链表

在前几章对C语言的学习中&#xff0c;我们学到了&#xff1a;基本的C语法和简单算法面向过程的编程思想而在数据结构这一篇章&#xff0c;我们将要学习&#xff1a;常用的数据存储结构算法面向对象的编程思想数据结构在正式开始学习之前&#xff0c;我们先来了解一下什么是数据…

win10任务栏出问题了,原来是wincompressbar导致的

问题描述兄弟们客户说自己电脑现在有问题了&#xff0c;任务栏显示的都不对&#xff0c;和之前的都不一样&#xff0c;现在使用起来非常难受&#xff0c;我们来看一下&#xff0c;这到底是什么问题吧&#xff01;到客户现场&#xff0c;查看发现&#xff0c;客户桌面系统最底下…

FFmpegHandler 功能解析,C语言程序化设计与C++面向对象设计的核心差异

FFmpegHandler 功能解析 本文件记录了关于 FFmpegHandler 类中核心函数工作流程的详细解释。Q: FFmpeg逐帧解码&#xff0c;FFmpegHandler::openVideo 和 FFmpegHandler::readAVFrame 这两个函数都分别做了什么&#xff1f; A: 可以把整个过程想象成“准备播放一部电影”&#…

Codeforces Round 1039 (Div. 2) A-C

A. Recycling Center题目大意 给你n个垃圾袋&#xff0c;每个垃圾袋有一个重量 在每秒钟&#xff0c;你可以选择一个垃圾袋&#xff0c;如果他的重量小于等于c&#xff0c;那么你可以不花费硬币丢掉它 当你丢掉一个垃圾袋后&#xff0c;其他垃圾袋在这一秒重量会翻倍 问最少花费…

【设计模式】 原则

单一职责原则 对于一个类而言&#xff0c;有且仅有一个引起他变化的原因或者说&#xff0c;一个类只负责一个职责 如果一个类承担的职责过多&#xff0c;那么这些职责放在一起耦合度太高了&#xff0c;一个职责的变化可能会影响这个类其他职责的能力。 所以我们在做软件设计的时…

windows11右键菜单新增项增加drawio文件,使用draw.io

目录1.新建空白模板2.建立注册表文件1.新建空白模板 这里我们的模板文件路径为 D:\Software\drawio\template.drawio 2.建立注册表文件 首先新建一个.txt文件&#xff0c;我这里取名为menulize.txt&#xff0c;然后将下面的内容复制到.txt文件中 Windows Registry Editor Ver…