diff --git a/app/admin/controller/SkuController.php b/app/admin/controller/SkuController.php index 9673a67..3730da4 100644 --- a/app/admin/controller/SkuController.php +++ b/app/admin/controller/SkuController.php @@ -256,10 +256,7 @@ class SkuController extends BaseController } /** - * docx 转 HTML(自动上传图片到 OSS) - * - * @param string $docxPath docx 文件绝对路径 - * @return string HTML + * docx 转 HTML(富文本可用,图片上传 OSS) */ function docxToHtmlWithImages(string $docxPath): string { @@ -267,238 +264,92 @@ class SkuController extends BaseController return ''; } - try { - // 1. 创建临时目录 - $tmpDir = runtime_path('tmp/' . uniqid('docx_', true)); - if (!is_dir($tmpDir)) { - mkdir($tmpDir, 0755, true); - } + // 1. 加载 docx + $phpWord = WordIOFactory::load($docxPath, 'Word2007'); - // 2. 解压 docx 文件(docx 本质上是 zip 文件) - $zip = new ZipArchive(); - if ($zip->open($docxPath) === true) { - $zip->extractTo($tmpDir); - $zip->close(); - } else { - throw new Exception('无法解压 docx 文件'); - } - - // 3. 读取文档内容 - $documentXml = $tmpDir . '/word/document.xml'; - if (!is_file($documentXml)) { - throw new Exception('文档内容文件不存在'); - } - - // 4. 创建 DOM 对象 - $dom = new DOMDocument(); - $dom->load($documentXml); - - // 5. 处理图片 - $uploadService = new UploadService(); - $images = $dom->getElementsByTagName('drawing'); - - foreach ($images as $image) { - // 查找图片引用 - $blip = $image->getElementsByTagName('blip'); - if ($blip->length > 0) { - $embedId = $blip->item(0)->getAttribute('r:embed'); - if ($embedId) { - // 查找关系文件中的图片路径 - $relationshipsXml = $tmpDir . '/word/_rels/document.xml.rels'; - if (is_file($relationshipsXml)) { - $relsDom = new DOMDocument(); - $relsDom->load($relationshipsXml); - - $xpath = new DOMXPath($relsDom); - $xpath->registerNamespace('r', 'http://schemas.openxmlformats.org/package/2006/relationships'); - - $query = "//r:Relationship[@Id='$embedId']"; - $nodes = $xpath->query($query); - - if ($nodes->length > 0) { - $target = $nodes->item(0)->getAttribute('Target'); - $imagePath = $tmpDir . '/word/' . $target; - - if (is_file($imagePath)) { - // 上传到 OSS - $ossUrl = $uploadService->uploadLocalFile([ - 'path' => $imagePath, - 'scene' => 'desc' - ]); - - // 创建 img 标签 - $imgNode = $dom->createElement('img'); - $imgNode->setAttribute('src', $ossUrl); - $imgNode->setAttribute('alt', '图片'); - - // 替换 drawing 标签 - $image->parentNode->replaceChild($imgNode, $image); - } - } - } - } - } - } - - // 6. 将文档内容转换为 HTML - $htmlContent = $this->convertDocumentXmlToHtml($dom, $tmpDir); - - // 7. 清理 HTML - $htmlContent = $this->cleanHtmlContent($htmlContent); - - // 8. 清理临时目录 - $this->deleteDirectory($tmpDir); - - return $htmlContent; - - } catch (\Exception $e) { - // 清理临时目录 - if (isset($tmpDir) && is_dir($tmpDir)) { - $this->deleteDirectory($tmpDir); - } - - error_log("docx转HTML失败: " . $e->getMessage()); - return ''; - } - } - - /** - * 将 document.xml 转换为 HTML - */ - private function convertDocumentXmlToHtml(DOMDocument $dom, string $tmpDir): string - { - $xpath = new DOMXPath($dom); - $xpath->registerNamespace('w', 'http://schemas.openxmlformats.org/wordprocessingml/2006/main'); - - $html = ''; - $bodyNodes = $xpath->query('//w:body/w:*'); - - foreach ($bodyNodes as $node) { - $html .= $this->convertNodeToHtml($node, $xpath); + // 2. 临时目录(非常重要) + $tmpDir = runtime_path('tmp/' . uniqid()); + if (!is_dir($tmpDir)) { + mkdir($tmpDir, 0755, true); } - return $html; - } + $tmpHtml = $tmpDir . '/index.html'; - /** - * 将 XML 节点转换为 HTML - */ - private function convertNodeToHtml(DOMNode $node, DOMXPath $xpath): string - { - $html = ''; - $nodeName = $node->nodeName; + // 3. 导出 HTML + $writer = WordIOFactory::createWriter($phpWord, 'HTML'); + $writer->save($tmpHtml); - switch ($nodeName) { - case 'w:p': // 段落 - $html .= '
'; - $textNodes = $xpath->query('.//w:t', $node); - foreach ($textNodes as $textNode) { - $html .= htmlspecialchars($textNode->nodeValue); - } - $html .= '
'; - break; + $html = file_get_contents($tmpHtml); - case 'w:tbl': // 表格 - $html .= '| '; - $cellTextNodes = $xpath->query('.//w:t', $cell); - foreach ($cellTextNodes as $cellText) { - $html .= htmlspecialchars($cellText->nodeValue); - } - $html .= ' | '; - } - $html .= '
\s*<\/p>/', '', $html);
-
- // 3. 确保图片有正确的样式
+ // 5. 处理图片
$html = preg_replace_callback(
- '/]+)>/',
- function($matches) {
- $attrs = $matches[1];
+ '/
]+src="([^"]+)"[^>]*>/i',
+ function ($matches) use ($tmpDir, $uploadService) {
+ $src = $matches[1];
- // 检查是否有样式
- if (!str_contains($attrs, 'style=')) {
- return '
';
+ // 只处理本地图片
+ if (preg_match('#^(http|https|data:)#i', $src)) {
+ return $matches[0];
}
- return '
';
+ $localImg = realpath($tmpDir . '/' . $src);
+ if (!$localImg || !is_file($localImg)) {
+ return $matches[0];
+ }
+
+ // 上传 OSS
+ $ossUrl = $uploadService->uploadLocalFile([
+ 'path' => $localImg,
+ 'scene' => 'desc'
+ ]);
+
+ return str_replace($src, $ossUrl, $matches[0]);
},
$html
);
+ // 6. 清理临时目录
+ $this->deleteDir($tmpDir);
+
return trim($html);
}
- /**
- * 删除目录
- */
- private function deleteDirectory(string $dir): bool
+ function cleanWordHtml(string $html): string
{
- if (!is_dir($dir)) {
- return false;
+ // 删除 Word 注释
+ $html = preg_replace('//', '', $html);
+
+ // 删除 style / meta / link
+ $html = preg_replace('/<(style|meta|link)[^>]*>.*?<\/\1>/is', '', $html);
+
+ // 删除 xml 标签
+ $html = preg_replace('/