You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel实现客户端上传Word文件导入试题及提取图片咨询

刚好我之前在项目里做过类似的需求——用PHPWord处理Word文档导入选择题+提取图片,结合Laravel的场景,给你梳理下具体的实现步骤和代码示例,应该能直接套用:

一、先搞定PHPWord的安装与基础配置

首先在你的Laravel项目里通过Composer安装PHPWord:

composer require phpoffice/phpword

安装完成后就可以直接调用它的API来加载和解析Word文档了。

二、核心逻辑:解析Word中的题目与选项

你之前处理TXT的逻辑是「含?的行是题目,后续行到下一题是选项,带*的是正确答案」,在Word里我们可以通过遍历文档的段落元素来实现同样的逻辑——PHPWord会把Word内容拆分成Section(章节)和Element(元素,比如文本段、图片等),我们只需要筛选文本元素做判断:

use PhpOffice\PhpWord\IOFactory;

// 假设已获取上传文件的存储路径(Laravel中可通过$request->file('word_file')->store('temp')得到)
$filePath = storage_path('app/temp/uploaded_questions.docx');

$phpWord = IOFactory::load($filePath);

$questions = [];
$currentQuestion = null;

// 遍历所有章节
foreach ($phpWord->getSections() as $section) {
    // 遍历章节内的所有元素
    foreach ($section->getElements() as $element) {
        // 只处理文本段落(Word里的文本通常包裹在TextRun里)
        if ($element instanceof \PhpOffice\PhpWord\Element\TextRun) {
            $textContent = '';
            // 拼接TextRun内的所有文本片段(避免格式拆分导致的文本不完整)
            foreach ($element->getElements() as $textPiece) {
                if ($textPiece instanceof \PhpOffice\PhpWord\Element\Text) {
                    $textContent .= $textPiece->getText();
                }
            }
            $textContent = trim($textContent);

            // 判断是否是题目(包含问号)
            if (str_contains($textContent, '?')) {
                // 先把上一个未完成的题目存入数组
                if ($currentQuestion) {
                    $questions[] = $currentQuestion;
                }
                // 初始化新的题目结构
                $currentQuestion = [
                    'title' => $textContent,
                    'options' => [],
                    'correct_option' => '',
                    'images' => [] // 预留存储关联图片的字段
                ];
            } elseif ($currentQuestion && !empty($textContent)) {
                // 处理选项,识别带*的正确答案
                $isCorrect = false;
                $optionText = $textContent;
                if (str_starts_with($textContent, '*')) {
                    $isCorrect = true;
                    $optionText = trim(substr($textContent, 1));
                }
                $currentQuestion['options'][] = $optionText;
                // 记录正确选项的索引(比如第0个选项是A的话,索引对应选项位置)
                if ($isCorrect) {
                    $currentQuestion['correct_option'] = count($currentQuestion['options']) - 1;
                }
            }
        }
    }
}

// 把最后一个题目加入结果数组
if ($currentQuestion) {
    $questions[] = $currentQuestion;
}
三、提取Word中的图片并关联题目

Word里的图片会被PHPWord识别为Image类型的元素,我们可以在遍历元素时同步处理图片,将其保存到Laravel的存储目录,并关联到当前的题目:

在上面的元素遍历循环中,加入图片处理的分支:

// 新增:处理图片元素
elseif ($element instanceof \PhpOffice\PhpWord\Element\Image) {
    // 获取图片的base64编码数据
    $imageRawData = $element->getImageStringData();
    if ($imageRawData) {
        // 生成唯一文件名,避免重复
        $fileName = time() . '_' . uniqid() . '.' . $element->getImageExtension();
        // 定义存储路径(这里存在public目录,方便前端访问)
        $savePath = storage_path('app/public/exam_images/' . $fileName);
        // 确保目录存在
        if (!file_exists(dirname($savePath))) {
            mkdir(dirname($savePath), 0755, true);
        }
        // 解码并保存图片
        file_put_contents($savePath, base64_decode($imageRawData));
        // 把图片的访问路径关联到当前题目(需先执行php artisan storage:link创建软链接)
        if ($currentQuestion) {
            $currentQuestion['images'][] = '/storage/exam_images/' . $fileName;
        }
    }
}
四、整合到Laravel的上传接口

把上面的逻辑封装到控制器方法里,处理前端的上传请求,最终返回和你之前TXT导入一致的返回格式:

use Illuminate\Http\Request;
use PhpOffice\PhpWord\IOFactory;

public function importWordQuestions(Request $request)
{
    // 验证上传文件格式
    $request->validate([
        'word_file' => 'required|mimes:docx,doc'
    ]);

    // 保存上传的Word文件到临时目录
    $uploadedFile = $request->file('word_file');
    $tempFilePath = $uploadedFile->store('temp');
    $fullFilePath = storage_path('app/' . $tempFilePath);

    // 执行解析逻辑(就是上面的代码)
    $phpWord = IOFactory::load($fullFilePath);
    $questions = [];
    $currentQuestion = null;

    foreach ($phpWord->getSections() as $section) {
        foreach ($section->getElements() as $element) {
            if ($element instanceof \PhpOffice\PhpWord\Element\TextRun) {
                // 文本处理逻辑(同上)
                $textContent = '';
                foreach ($element->getElements() as $textPiece) {
                    if ($textPiece instanceof \PhpOffice\PhpWord\Element\Text) {
                        $textContent .= $textPiece->getText();
                    }
                }
                $textContent = trim($textContent);

                if (str_contains($textContent, '?')) {
                    if ($currentQuestion) {
                        $questions[] = $currentQuestion;
                    }
                    $currentQuestion = [
                        'title' => $textContent,
                        'options' => [],
                        'correct_option' => '',
                        'images' => []
                    ];
                } elseif ($currentQuestion && !empty($textContent)) {
                    $isCorrect = false;
                    $optionText = $textContent;
                    if (str_starts_with($textContent, '*')) {
                        $isCorrect = true;
                        $optionText = trim(substr($textContent, 1));
                    }
                    $currentQuestion['options'][] = $optionText;
                    if ($isCorrect) {
                        $currentQuestion['correct_option'] = count($currentQuestion['options']) - 1;
                    }
                }
            } elseif ($element instanceof \PhpOffice\PhpWord\Element\Image) {
                // 图片处理逻辑(同上)
                $imageRawData = $element->getImageStringData();
                if ($imageRawData) {
                    $fileName = time() . '_' . uniqid() . '.' . $element->getImageExtension();
                    $savePath = storage_path('app/public/exam_images/' . $fileName);
                    if (!file_exists(dirname($savePath))) {
                        mkdir(dirname($savePath), 0755, true);
                    }
                    file_put_contents($savePath, base64_decode($imageRawData));
                    if ($currentQuestion) {
                        $currentQuestion['images'][] = '/storage/exam_images/' . $fileName;
                    }
                }
            }
        }
    }

    if ($currentQuestion) {
        $questions[] = $currentQuestion;
    }

    // 返回和TXT导入一致的格式
    return response()->json([
        'success' => true,
        'data' => $questions
    ]);
}
五、几个关键注意事项
  • 创建存储软链接:执行php artisan storage:link,让/storage目录可以被前端访问到;
  • 格式兼容:PHPWord支持.docx和.doc格式,但.doc的解析依赖一些底层库,建议优先要求用户上传.docx;
  • 特殊格式处理:如果Word里的题目/选项有复杂格式(比如编号、加粗),可以根据需求调整文本拼接逻辑,或者用正则匹配过滤无关格式;
  • 大文件优化:如果上传的Word文件很大,可以考虑增加内存限制,或者分批处理元素。

内容的提问来源于stack exchange,提问作者Netcom Ask

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 18:32:58