Symfony6+TinyMCE富文本textarea实现及图片持久化问题
Symfony + TinyMCE 富文本图片URL持久化解决方案
问题核心
你需要实现Symfony结合TinyMCE的富文本编辑器,支持多图片上传、文本样式渲染,最终将包含正确图片URL的富文本内容持久化到数据库。当前配置中TinyMCE已开启图片上传,但后端未正确处理图片上传请求,导致富文本中的图片无法替换为服务器可访问的URL。
分步解决方案
1. 新增TinyMCE图片上传专用控制器
创建独立路由处理图片上传请求,返回TinyMCE要求的格式(包含location字段的JSON),确保上传后的图片生成可访问URL。
#[Route('/admin/tinymce/upload', name: 'app_admin_tinymce_upload', methods: ['POST'])] public function tinymceUpload(Request $request, UploadHandler $uploadHandler, UrlGeneratorInterface $urlGenerator): JsonResponse { $file = $request->files->get('file'); if (!$file || !$file instanceof UploadedFile) { return new JsonResponse(['error' => 'Aucun fichier téléchargé'], 400); } // 调用你的上传处理器保存图片,调整参数匹配你的业务逻辑 $fileName = $uploadHandler->uploadFile($file, 'tinymce_images'); // 生成图片的绝对访问URL $imageUrl = $urlGenerator->generate('app_image_tinymce', ['fileName' => $fileName], UrlGeneratorInterface::ABSOLUTE_URL); // TinyMCE要求返回的JSON必须包含location字段 return new JsonResponse(['location' => $imageUrl]); }
2. 配置图片访问路由
添加路由用于前端访问上传后的图片:
#[Route('/images/tinymce/{fileName}', name: 'app_image_tinymce')] public function serveTinymceImage(string $fileName, Filesystem $filesystem): Response { $filePath = $this->getParameter('tinymce_images_directory') . '/' . $fileName; if (!$filesystem->exists($filePath)) { throw $this->createNotFoundException('Image not found'); } $mimeType = mime_content_type($filePath); return new BinaryFileResponse($filePath, 200, ['Content-Type' => $mimeType]); }
在services.yaml中配置图片存储目录参数:
parameters: tinymce_images_directory: '%kernel.project_dir%/public/images/tinymce'
3. 修改TinyMCE配置
将images_upload_url替换为实际的上传路由,确保编辑器自动上传图片并替换为服务器URL:
<script type="text/javascript" src="https://cdn.tiny.cloud/1/000e98r5ejnk39y3tkknc2y0b8qh895bn5qv1jrc6a5v3ceu/tinymce/5/tinymce.min.js"></script> <script> tinymce.init({ selector: '#portrait_form_description', language: 'fr', plugins: 'image', toolbar: 'undo redo | link image', image_title: true, automatic_uploads: true, valid_elements : '+*[*]', // 替换为上传路由的Twig路径 images_upload_url: "{{ path('app_admin_tinymce_upload') }}", file_picker_types: 'image', file_picker_callback: function (cb, value, meta) { var input = document.createElement('input'); input.setAttribute('type', 'file'); input.setAttribute('accept', 'image/*'); input.onchange = function () { var file = this.files[0]; var reader = new FileReader(); reader.onload = function () { var id = 'blobid' + (new Date()).getTime(); var blobCache = tinymce.activeEditor.editorUpload.blobCache; var base64 = reader.result.split(',')[1]; var blobInfo = blobCache.create(id, file, base64); blobCache.add(blobInfo); cb(blobInfo.blobUri(), { title: file.name }); }; reader.readAsDataURL(file); }; input.click(); }, content_style: 'body { font-family:Helvetica,Arial,sans-serif; font-size:14px }' }); </script>
4. 调整现有控制器逻辑
移除与富文本无关的独立图片处理代码,直接保存包含正确图片URL的富文本内容:
#[Route('/admin/portraits', name: 'app_admin_portraits')] public function portraits(Request $request): Response { $portrait = new Portrait(); $form = $this->createForm(PortraitFormType::class, $portrait); if($request->isXmlHttpRequest()){ $form->handleRequest($request); if ($form->isSubmitted() && $form->isValid()) { // 此时description字段已包含服务器可访问的图片URL,直接持久化 $this->em->persist($portrait); $this->em->flush(); $this->addFlash('success', "Portrait enregistré avec succéss"); return new JsonResponse(['success' => true, 'message' => "Portrait enregistré avec succès"]); } else { $errors = $this->getFormErrors($form); return new JsonResponse(['success' => false, 'errors' => $errors], 400); } } $portraits = $this->paginator->paginate($this->portraitRepository->findAll(), 1, 10); return $this->render('admin/portraits.html.twig', [ 'formPortrait' => $form->createView(), 'portraits' => $portraits, ]); }
5. 确保实体字段支持长文本
在Portrait实体中,将description字段设置为text类型以存储富文本内容:
#[ORM\Column(type: 'text')] private ?string $description = null; public function getDescription(): ?string { return $this->description; } public function setDescription(?string $description): self { $this->description = $description; return $this; }
流程说明
- 用户在编辑器中插入图片时,TinyMCE调用
file_picker_callback选择本地文件 - 编辑器自动将文件上传到配置的
images_upload_url路由 - 后端处理上传,返回图片的绝对访问URL
- TinyMCE将编辑器中的Blob URI替换为返回的URL
- 表单提交时,
description字段已包含正确的图片URL,直接保存到数据库即可
内容的提问来源于stack exchange,提问作者kasali
相关产品推荐
相关产品推荐

