php完整處理word中表單數據的方法

使用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…