
1、服务器环境配置要求
php
# 安装依赖
yum install -y ImageMagick ImageMagick-devel
# 安装PHP扩展
pecl install imagick
# 重启PHP服务
systemctl restart php-fpm
2、安装 SVG 依赖库
php
# CentOS 7/8
yum install -y librsvg2 librsvg2-devel
# 如果是 Debian/Ubuntu
# apt-get install -y librsvg2-bin librsvg2-dev
php
<?php
// 临时图片目录(addHtml会自动下载图片到这里)
$tempDir = $_SERVER['DOCUMENT_ROOT'] . '/temp/temp_images/';
// 新增:临时文件路径清单,用于统一清理SVG转换的PNG文件
$tempFiles = [];
/**
* 初始化临时目录
*/
function initTempDir()
{
global $tempDir;
if (!is_dir($tempDir)) {
mkdir($tempDir, 0755, true);
}
if (!is_writable($tempDir)) {
throw new Exception("临时目录不可写:{$tempDir}");
}
}
/**
* 清理临时目录中的图片文件
* addHtml会自动下载图片到临时目录,需要手动清理
*/
function cleanTempDir()
{
global $tempDir;
if (is_dir($tempDir)) {
$files = glob($tempDir . '/*');
foreach ($files as $file) {
if (is_file($file)) {
@unlink($file);
}
}
}
}
/**
* 新增:清理SVG转换生成的临时PNG文件
*/
function cleanSvgTempFiles()
{
global $tempFiles;
foreach ($tempFiles as $key => $file) {
if (is_file($file)) {
@unlink($file); // @屏蔽删除不存在文件的警告
}
// 从清单移除已处理的路径,防止重复清理
unset($tempFiles[$key]);
}
}
/**
* 新增:将SVG图片转换为PNG格式
* @param string $svgUrl SVG图片URL
* @param string $savePath 转换后的PNG保存路径
* @return string|false 转换后的PNG本地路径,失败返回false
*/
function svgToPng($svgUrl, $savePath)
{
global $tempDir, $tempFiles;
$tempFiles[] = $savePath;
// 新增:先清空目标文件(避免残留空文件)
if (file_exists($savePath)) {
unlink($savePath);
}
// 1. 下载 SVG 内容
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $svgUrl);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
curl_setopt($ch, CURLOPT_TIMEOUT, 15);
$svgContent = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curlError = curl_error($ch);
curl_close($ch);
if ($curlError || $httpCode != 200 || empty($svgContent)) {
error_log("SVG下载失败:URL={$svgUrl},HTTP状态码={$httpCode},curl错误={$curlError}");
return false;
}
// ========== 核心修改:移除 SVG 命名空间前缀 ==========
// 1. 移除所有 ns0: / ns1: 前缀
$svgContent = preg_replace('/ns[0-9]+:/', '', $svgContent);
// 2. 清理命名空间声明(保留核心 xmlns)
$svgContent = preg_replace('/xmlns:ns[0-9]+="[^"]+"/', '', $svgContent);
// 3. 确保根标签是 <svg> 而不是 <ns0:svg>
$svgContent = preg_replace('/<\/?ns[0-9]+:svg/', '<$1svg', $svgContent);
// 4. 修复 href 属性(ns1:href → xlink:href,兼容 SVG 标准)
$svgContent = str_replace('href="#', 'xlink:href="#', $svgContent);
// 5. 补充 xlink 命名空间(确保 href 生效)
if (strpos($svgContent, 'xmlns:xlink') === false) {
$svgContent = preg_replace('/<svg /', '<svg xmlns:xlink="http://www.w3.org/1999/xlink" ', $svgContent, 1);
}
// 2. 使用Imagick转换SVG为PNG
try {
$imagick = new \Imagick();
$imagick->setResolution(300, 300);
$imagick->setBackgroundColor(new \ImagickPixel('white'));
// 关键:设置 SVG 解析器(强制使用 RSVG 解析)
$imagick->setOption('svg:use-rsvg', 'true');
$imagick->readImageBlob($svgContent);
$imagick->setImageFormat('png');
$imagick->writeImage($savePath);
// 增强校验:文件存在且大小>0
if (!file_exists($savePath) || filesize($savePath) < 10) { // 至少10字节(避免空文件)
error_log("PNG文件无效:{$savePath},大小:" . filesize($savePath));
if (file_exists($savePath)) {
unlink($savePath); // 删除空文件
}
return false;
}
error_log("SVG 转 PNG 成功:生成文件={$savePath},大小=" . filesize($savePath) . "字节");
$imagick->clear();
$imagick->destroy();
return $savePath;
} catch (Exception $e) {
error_log("SVG转PNG异常:" . $e->getMessage() . ",URL:" . $svgUrl);
// 清理临时文件
if (file_exists($savePath)) {
unlink($savePath);
}
return false;
}
}
/**
* 修复图片URL中的空格和格式问题
* @param string $url 原始URL
* @return string 修复后的URL
*/
function fixImageUrl($url)
{
// 移除URL中的所有空格(包括协议后的空格,如https: // -> https://)
$url = preg_replace('/\s+/', '', $url);
// 确保URL是合法的http/https链接
if (strpos($url, 'http') !== 0) {
$url = 'https://' . ltrim($url, '/');
}
// URL编码特殊字符
$parsed = parse_url($url);
if ($parsed && isset($parsed['path'])) {
$pathParts = explode('/', $parsed['path']);
$encodedPath = [];
foreach ($pathParts as $part) {
$encodedPath[] = rawurlencode($part);
}
$url = $parsed['scheme'] . '://' . $parsed['host'] . implode('/', $encodedPath);
if (isset($parsed['query'])) {
$url .= '?' . $parsed['query'];
}
}
return $url;
}
/**
* 规范化HTML,修复XML解析问题,确保符合DOMDocument解析要求
* @param string $html 原始HTML
* @return string 规范化后的HTML
*/
/**
* 规范化HTML,修复XML解析问题,确保符合DOMDocument解析要求
* 核心优化:修复图片路径格式,确保PhpWord能正确加载图片
* @param string $html 原始HTML
* @return string 规范化后的HTML
*/
function normalizeHtmlForPhpWord($html)
{
global $tempDir;
// 1. 移除控制字符(避免DOM解析报错)
$html = preg_replace('/[\x00-\x1F\x7F]/', '', $html);
// 2. 移除换行/制表符(保留单个空格)
$html = str_replace(["\r\n", "\r", "\n", "\t"], ' ', $html);
// 3. 合并多余空格
$html = preg_replace('/\s+/', ' ', $html);
// 4. 彻底清理所有样式相关内容(关键:完全移除原有样式)
$html = preg_replace('/<style[^>]*>.*?<\/style>/is', '', $html); // 移除style标签
$html = preg_replace('/\s+(style|font-size|font-family|line-height|font-weight|color)="[^"]*"/i', '', $html); // 移除内联样式属性
$html = preg_replace('/<font[^>]*>/i', '', $html); // 移除font开始标签
$html = preg_replace('/<\/font>/i', '', $html); // 移除font结束标签
$html = preg_replace('/<span[^>]*>/i', '', $html); // 移除span开始标签
$html = preg_replace('/<\/span>/i', '', $html); // 移除span结束标签
// 5. 核心修改:为整个HTML包裹统一的样式容器(强制字体/行高)
$html = '<div style="font-family: 宋体; font-size: 12pt; line-height: 1.5;">' . $html . '</div>';
// 6. 修复img标签的属性格式(核心:路径格式修复 + 定高不定宽 + 动态高度)
$html = preg_replace_callback('/<img\s+[^>]*src\s*=\s*["\']?([^"\'>]+)["\']?[^>]*>/i', function ($matches) {
global $tempDir;
$imgTag = $matches[0];
$src = $matches[1];
// 修复图片URL(清理空格、补全协议)
$fixedSrc = fixImageUrl($src);
// ========== 核心:图片路径格式修复 ==========
// 定义最大/最小高度(动态高度判断)
$MAX_HEIGHT = 200; // 最大高度px
$MIN_HEIGHT = 50; // 最小高度px
$targetHeight = $MAX_HEIGHT;
// SVG转PNG逻辑 + 本地路径修复
$fileExt = strtolower(pathinfo(parse_url($fixedSrc, PHP_URL_PATH), PATHINFO_EXTENSION));
if ($fileExt === 'svg') {
$pngFileName = uniqid('svg_') . '.png';
$pngFilePath = $tempDir . $pngFileName;
// 确保临时目录存在且可写
if (!is_dir($tempDir)) {
mkdir($tempDir, 0755, true);
}
// 转换SVG为PNG
if (svgToPng($fixedSrc, $pngFilePath)) {
// 修复1:获取真实绝对路径(兼容Windows/Linux)
$pngRealPath = realpath($pngFilePath);
if ($pngRealPath === false) {
error_log("SVG转换后的PNG路径无效:{$pngFilePath}");
return '';
}
// 修复2:添加标准file://协议(必须是3个/,兼容不同系统)
$fixedSrc = 'file:///' . str_replace(DIRECTORY_SEPARATOR, '/', $pngRealPath);
error_log("SVG转换后路径:{$fixedSrc}");
} else {
error_log("SVG转PNG失败,原URL:{$fixedSrc}");
return ''; // 转换失败移除图片标签
}
} else {
// 非SVG图片:区分远程URL和本地路径
if (strpos($fixedSrc, 'http') === 0) {
// 远程图片:确保URL格式正确(已通过fixImageUrl修复)
error_log("远程图片路径:{$fixedSrc}");
} else {
// 本地图片:修复路径格式
// 修复1:拼接网站根目录,获取真实绝对路径
$localPath = realpath($_SERVER['DOCUMENT_ROOT'] . '/' . ltrim($fixedSrc, '/'));
if ($localPath === false || !file_exists($localPath)) {
error_log("本地图片不存在:{$fixedSrc} → 真实路径:{$localPath}");
return ''; // 路径无效移除图片标签
}
// 修复2:添加标准file://协议
$fixedSrc = 'file:///' . str_replace(DIRECTORY_SEPARATOR, '/', $localPath);
error_log("本地图片修复后路径:{$fixedSrc}");
}
}
// ========== 动态高度计算(定高不定宽) ==========
$imageInfo = false;
// 获取图片尺寸(区分file协议和远程URL)
if (strpos($fixedSrc, 'file://') === 0) {
// 本地文件:去掉file:///前缀
$localFile = substr($fixedSrc, 8);
$imageInfo = @getimagesize($localFile);
} else if (strpos($fixedSrc, 'http') === 0) {
// 远程文件:需开启allow_url_fopen
$imageInfo = @getimagesize($fixedSrc);
}
// 计算目标高度
if ($imageInfo !== false && is_array($imageInfo)) {
$originalHeight = $imageInfo[1];
// 原图高度 < 最大高度 → 保留原图高度;否则用最大高度
$targetHeight = $originalHeight < $MAX_HEIGHT ? $originalHeight : $MAX_HEIGHT;
// 确保不小于最小高度
$targetHeight = max($targetHeight, $MIN_HEIGHT);
} else {
error_log("无法获取图片尺寸:{$fixedSrc}");
}
// ========== 图片标签属性最终调整 ==========
// 1. 替换src为修复后的路径
$imgTag = preg_replace('/src\s*=\s*["\']?[^"\'>]+["\']?/i', 'src="' . $fixedSrc . '"', $imgTag);
// 2. 移除所有width属性(定高不定宽)
$imgTag = preg_replace('/\s+width\s*=\s*["\']?[^"\'>]+["\']?/i', '', $imgTag);
// 3. 移除原有height属性,设置动态高度
$imgTag = preg_replace('/\s+height\s*=\s*["\']?[^"\'>]+["\']?/i', '', $imgTag);
$imgTag = preg_replace('/(<img[^>]+)\/?>/i', '$1 height="' . $targetHeight . '" />', $imgTag);
// 4. 确保属性有引号包裹,标签自闭合(XML规范)
$imgTag = preg_replace('/(\w+)\s*=\s*([^\s>"]+)(?=\s|>)/i', '$1="$2"', $imgTag);
if (substr(trim($imgTag), -1) != '/') {
$imgTag = rtrim($imgTag, '>') . ' />';
}
return $imgTag;
}, $html);
// 7. 修复所有HTML属性的格式(确保属性名=值都有引号)
$html = preg_replace('/(\w+)\s*=\s*([^\s>"]+)(?=\s|>)/i', '$1="$2"', $html);
// 8. 清理首尾空格
return trim($html);
}
// 数据校验
if (!$rs = _get_one_tj('a_ai_fenxi', 'id="' . $detailId . '"')) {
json('信息发生变化,请刷新页面再试!', 888);
}
$rszl = _get_one('a_ziliao', $rs["member_id"], 'member_id');
// 获取/刷新知识点和统计数据
$knowledgearr = $rs["knowledgearr"] ? json_decode($rs["knowledgearr"], true) : array();
$statsarr = $rs["statsarr"] ? json_decode($rs["statsarr"], true) : array();
if (count($knowledgearr) == 0) {
$url = 'http://121.89.200.131:5678/get_recommendations?member_id=' . $userid;
$result = curl_get_al_fenxi($url);
$array = json_decode($result["data"], true);
$knowledgearr = $array["knowledge_recommendations"];
$statsarr = $array["user_stats"];
// 存储更新后的数据
$dataArr = array();
$dataArr["knowledgearr"] = json_encode($knowledgearr, JSON_UNESCAPED_UNICODE);
$dataArr["statsarr"] = json_encode($statsarr, JSON_UNESCAPED_UNICODE);
_update('a_ai_fenxi', $dataArr, $rs["id"]);
}
if ($rs["download_url_word"]) {
$infoArray["subjects"]["file"] = $swtBaseImg . $swtWjj . $rs["download_url_word"];
echo json_encode($infoArray);
exit;
}
// 配置内存限制和PhpWord
ini_set("memory_limit", "2048M");
require_once $_SERVER['DOCUMENT_ROOT'] . '/api/vendor/autoload.php';
$phpWord = new \PhpOffice\PhpWord\PhpWord();
$section = $phpWord->addSection();
// ========== 样式定义 ==========
/**
* 基础字体样式(微软雅黑)
* @param string|int $size 字号,默认20pt
* @param string $color 颜色,默认#000
* @param bool $bold 是否加粗,默认false
* @return array
*/
function addFontStyle($size = '20', $color = '#000', $bold = false)
{
return [
'name' => '微软雅黑',
'size' => $size,
'color' => $color,
'bold' => $bold
];
}
/**
* 宋体字体样式
* @param string|int $size 字号,默认12pt
* @param string $color 颜色,默认#000
* @param bool $bold 是否加粗,默认false
* @return array
*/
function addFontStylesong($size = '12', $color = '#000', $bold = false)
{
return [
'name' => '宋体',
'size' => $size,
'color' => $color,
'bold' => $bold
];
}
/**
* 微软雅黑 + 四号字(14pt)
* @param string $color 颜色,默认#000
* @param bool $bold 是否加粗,默认false
* @return array
*/
function addFontStyleSimHeiFourth($color = '#000', $bold = false)
{
return addFontStyle(14, $color, $bold);
}
/**
* 宋体 + 小四(12pt) + 加粗
* @param string $color 颜色,默认#000
* @return array
*/
function addFontStyleSimSunSmallFourth($color = '#000')
{
return addFontStylesong(12, $color, true);
}
/**
* 1.00倍行距段落样式
* @param string $align 对齐方式,默认left
* @param int $spaceAfter 段后间距,默认0
* @return array
*/
function addParagraphStyleLineHeight1($align = 'left', $spaceAfter = 0)
{
return [
'align' => $align,
'lineHeight' => 1.00,
'lineHeightRule' => 'multiple',
'spaceAfter' => $spaceAfter,
'spaceBefore' => 0
];
}
/**
* 1.5倍行距段落样式
* @param string $align 对齐方式,默认left
* @param int $spaceAfter 段后间距,默认0
* @return array
*/
function addParagraphStyleLineHeight1_5($align = 'left', $spaceAfter = 0)
{
return [
'align' => $align,
'lineHeight' => 1.5,
'lineHeightRule' => 'multiple',
'spaceAfter' => $spaceAfter,
'spaceBefore' => 0
];
}
/**
* 给HTML文本中第一个<p>标签内的文本添加序号前缀
* @param string $html 原始HTML文本(需包含<p>标签)
* @return string 处理后的HTML文本
*/
function addNumberPrefixToFirstPtag($html, $prefix = '')
{
// 空值处理
if (empty($html) || empty($prefix)) {
return $html;
}
// ========== 优化后的匹配规则 ==========
// 1. 先清理HTML中的多余空白符(避免干扰匹配)
$html = preg_replace('/\s+/', ' ', $html);
$html = trim($html);
// 2. 优化正则:
// - 放宽<p>标签匹配范围,兼容各种属性和空白符
// - 使用更稳定的匹配方式,避免非贪婪匹配提前终止
$pattern = '/(<p[^>]*>)([^<]*)/i';
// 替换逻辑:在<p>标签后直接添加前缀(匹配<p>标签 + 标签后第一个非<字符段)
$html = preg_replace('/<p>(.*?)<\/p>/', '<p>' . $prefix . '$1</p>', $html, 1);
return $html;
}
// 替换所有Html::addHtml调用,添加异常捕获
function safeAddHtml($section, $html, $htmlOptions)
{
try {
// 先过滤空HTML
if (trim($html) === '') {
return;
}
\PhpOffice\PhpWord\Shared\Html::addHtml($section, $html, false, $htmlOptions);
} catch (\Exception $e) {
error_log("解析HTML失败:" . $e->getMessage() . ",HTML:" . $html);
// 跳过该段HTML,不中断导出
$section->addText('【图片加载失败】', addFontStylesong(12, '#ff0000'));
echo "解析HTML失败:" . $e->getMessage() . ",HTML:" . $html;
exit();
}
}
// 基础段落样式
$paragraphStyleCenter = ['align' => 'center', 'spaceAfter' => 20];
// addHtml配置(仅保留图片样式和基础解析配置)
$htmlOptions = [
// 禁用默认样式自动生成,优先使用HTML内的样式
'useDefaultStyles' => false
];
// 大写字母数组(A-Z)
$ABC = [];
for ($i = 65; $i < 91; $i++) {
$ABC[] = strtoupper(chr($i));
}
try {
// 初始化临时目录
initTempDir();
// 禁用DOMDocument的错误提示(解决解析警告)
libxml_use_internal_errors(true);
// ========== 文档内容构建 ==========
// 标题
$section->addText('博电线下班学员学习监督报告', addFontStyle(20, '#000', true), $paragraphStyleCenter);
$section->addTextBreak(1);
// 用户名和报告日期
$section->addText('用户名:' . $rszl["name"], addFontStyleSimHeiFourth(), addParagraphStyleLineHeight1('right'));
$section->addText('报告日期:' . $rs["title"], addFontStyleSimHeiFourth(), addParagraphStyleLineHeight1('right'));
$section->addTextBreak(1);
// 一、推荐知识点
$section->addText('一、推荐知识点', addFontStyleSimSunSmallFourth(), addParagraphStyleLineHeight1_5('left'));
$section->addTextBreak(1);
$knowledgearr_timu = array();
foreach ($knowledgearr as $iai => $rsai) {
$timu_title = '';
if ($rst = _get_one('php_tiku_timu', $rsai["knowledge_id"])) {
$xuhaoiai = ($iai + 1) . '、';
$timu_title = addNumberPrefixToFirstPtag($rst["title"], $xuhaoiai);
$timu_title = is_body_img_word_style($timu_title, $swtBaseImg);
}
// 规范化HTML
$timu_title = preg_replace('/[\x00-\x1F\x7F]/', '', $timu_title);
$timu_title = htmlspecialchars_decode($timu_title);
$timu_title = normalizeHtmlForPhpWord($timu_title);
// 添加知识点内容
safeAddHtml($section, $timu_title, $htmlOptions);
// 收集习题ID
foreach ($rsai["timus"] as $it => $rst) {
$knowledgearr_timu[] = $rst;
}
}
// 二、推荐习题
$section->addText('二、推荐习题', addFontStyleSimSunSmallFourth(), addParagraphStyleLineHeight1_5('left'));
$knowledgearr_timu_xuan = array();
$itmjie = 0;
foreach ($knowledgearr_timu as $itm => $rstms) {
if ($rstm = _get_one_tj('php_tiku_timu', 'id=' . $rstms["timu_id"] . ' and del=0 and types!=5')) {
// 处理题干
$titleitm = ($itm + 1) . '、';
$timu_txt = addNumberPrefixToFirstPtag($rstm["title"], $titleitm);
$timu_txt = is_body_img_word_style($timu_txt, $swtBaseImg);
$timu_txt = preg_replace('/[\x00-\x1F\x7F]/', '', $timu_txt);
$timu_txt = htmlspecialchars_decode($timu_txt);
$timu_txt = normalizeHtmlForPhpWord($timu_txt);
// 添加题干
safeAddHtml($section, $timu_txt, $htmlOptions);
// 处理选项/答案
if ($rstm["types"] != 3) {
$daan_txt = array();
if ($rstm["types"] != 2 && $rstm["types"] != 4) {
$arrx = _getallCachedByTj3('php_tiku_xuanxiang', "timu_id='" . $rstm["id"] . "'");
// 初始化daan5数组(修复未定义警告)
$daan5 = [];
foreach ($arrx as $ix => $rsx) {
$daan5[] = $rsx["title"];
if ($rstm["types"] != 5) {
// 标记正确答案
if ($rsx["correct"] == 1) {
$daan_txt[] = $ABC[$ix];
}
// 处理选项内容
$xuanfuhao = $ABC[$ix] . ':';
$xuantitle = addNumberPrefixToFirstPtag($rsx["title"], $xuanfuhao);
$xuantitle = is_body_img_word_style($xuantitle, $swtBaseImg);
$xuantitle = preg_replace('/[\x00-\x1F\x7F]/', '', $xuantitle);
$xuantitle = htmlspecialchars_decode($xuantitle);
$xuantitle = normalizeHtmlForPhpWord($xuantitle);
// 添加选项
safeAddHtml($section, $xuantitle, $htmlOptions);
}
}
// 存储答案
if ($rstm["types"] == 5) {
$title_5 = replaceContentsInBrackets($timu_txt, $daan5);
$knowledgearr_timu_xuan[$itmjie]["daan"] = $title_5;
} else {
$knowledgearr_timu_xuan[$itmjie]["daan"] = implode('', $daan_txt);
}
} elseif ($rstm["types"] == 2 || $rstm["types"] == 4) {
// 判断题/问答题选项
$section->addText(
'选项A:正确',
addFontStylesong(12), // 宋体 + 小四(12pt)
addParagraphStyleLineHeight1_5('left') // 1.5倍行高 + 左对齐
);
$section->addText(
'选项B:错误',
addFontStylesong(12), // 宋体 + 小四(12pt)
addParagraphStyleLineHeight1_5('left') // 1.5倍行高 + 左对齐
);
if ($rstm["types"] == 4) {
$knowledgearr_timu_xuan[$itmjie]["daan"] = 'A';
$xwenda_txt = is_body_img_word_style($rstm["wenda_txt"], $swtBaseImg);
$xwenda_txt = preg_replace('/[\x00-\x1F\x7F]/', '', $xwenda_txt);
$xwenda_txt = htmlspecialchars_decode($xwenda_txt);
$xwenda_txt = normalizeHtmlForPhpWord($xwenda_txt);
safeAddHtml($section, $xwenda_txt, $htmlOptions);
} elseif ($rstm["types"] == 2) {
$knowledgearr_timu_xuan[$itmjie]["daan"] = $rstm["tie_types3"] == 1 ? 'A' : 'B';
}
}
}
// 存储解析和视频信息
$knowledgearr_timu_xuan[$itmjie]["jiexi_txt"] = $rstm["jiexi_txt"];
$knowledgearr_timu_xuan[$itmjie]["media_id_txt"] = $rstm["media_id"] ? '有' : '无';
$itmjie++;
}
}
// 三、答案及解析情况
$section->addText('三、答案及解析情况', addFontStyleSimSunSmallFourth(), addParagraphStyleLineHeight1_5('left'));
foreach ($knowledgearr_timu_xuan as $itmx => $rstmsx) {
// 解析标题
$section->addText(
$itmx + 1 . '、解析:',
addFontStylesong(12), // 宋体 + 小四(12pt)
addParagraphStyleLineHeight1_5('left') // 1.5倍行高 + 左对齐
);
// 答案
$section->addText(
'答案:' . $rstmsx["daan"],
addFontStylesong(12), // 宋体 + 小四(12pt)
addParagraphStyleLineHeight1_5('left') // 1.5倍行高 + 左对齐
);
// 解析内容
$jiexifuhao = '文本解析:';
$jiexi_txt = addNumberPrefixToFirstPtag($rstmsx["jiexi_txt"], $jiexifuhao);
$jiexi_txt = is_body_img_word_style($jiexi_txt, $swtBaseImg);
$jiexi_txt = preg_replace('/[\x00-\x1F\x7F]/', '', $jiexi_txt);
$jiexi_txt = htmlspecialchars_decode($jiexi_txt);
$jiexi_txt = normalizeHtmlForPhpWord($jiexi_txt);
safeAddHtml($section, $jiexi_txt, $htmlOptions);
// 视频信息
$section->addText(
'是否有解析视频:' . $rstmsx["media_id_txt"],
addFontStylesong(12), // 宋体 + 小四(12pt)
addParagraphStyleLineHeight1_5('left') // 1.5倍行高 + 左对齐
);
}
// ========== 文件生成与上传 ==========
// 生成文件名
$datetime = date("Ymdhis", time());
$savename = "{$datetime}.docx";
$basPath = "/temp/up_word/" . date("Ymd") . "/";
// 创建Writer
$objWriter = \PhpOffice\PhpWord\IOFactory::createWriter($phpWord, 'Word2007');
// 输出到内存
ob_start();
$objWriter->save('php://output');
$file_content = ob_get_clean();
// 上传到远程服务器
$upload_url = $swtBaseAdmin . '/api/3.0.0/pcapi/word_url.php';
$post_data = [
'filename' => $savename,
'path' => $basPath,
'file_content' => $file_content,
'api_key' => 'your_very_secret_api_key' // 建议替换为真实密钥
];
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $upload_url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
// HTTPS兼容(如需)
// curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
// curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if (curl_errno($ch)) {
json('cURL 错误: ' . curl_error($ch), 200, 'error');
} else {
if ($http_code == 200) {
// 返回文件地址并更新数据库
$infoArray["subjects"]["file"] = $swtBaseImg . $swtWjj . $basPath . $savename;
$dataArrfenxi = ["download_url_word" => $basPath . $savename];
_update('a_ai_fenxi', $dataArrfenxi, $rs["id"]);
} else {
json("上传失败!HTTP 状态码: $http_code, 服务器响应: " . $response, 200, 'error');
}
}
curl_close($ch);
// 恢复DOM错误提示
libxml_use_internal_errors(false);
// 输出结果
echo json_encode($infoArray);
// 延迟清理:先输出响应,再清理临时文件(给PhpWord足够时间加载图片)
register_shutdown_function(function () {
cleanTempDir();
cleanSvgTempFiles();
});
exit;
} catch (Exception $e) {
// 异常处理
echo "执行出错:" . $e->getMessage() . "<br>";
echo "错误行号:" . $e->getLine() . "<br>";
// 恢复DOM错误提示
libxml_use_internal_errors(false);
// 延迟清理:先输出响应,再清理临时文件(给PhpWord足够时间加载图片)
register_shutdown_function(function () {
cleanTempDir();
cleanSvgTempFiles();
});
}