ASP.NET Core MVC中如何在文章段落间插入图片?
解决方案
1. 定义文章相关模型
扩展文章实体与DTO,支持富文本内容存储和图片上传关联:
文章实体(含图片关联)
public class Post : EntityBase { public string Title { get; set; } = default!; // 存储带图片标签的富文本内容 public string Content { get; set; } = default!; public int AuthorId { get; set; } public Author Author { get; set; } = default!; // 可选:存储文章关联的所有图片路径,便于后续管理 public ICollection<PostImage> PostImages { get; set; } = new List<PostImage>(); } // 文章图片关联实体(可选) public class PostImage : EntityBase { public string ImageUrl { get; set; } = default!; public int PostId { get; set; } public Post Post { get; set; } = default!; }
文章发布/编辑DTO
public class PostCreateDto { public string Title { get; set; } = default!; // 存储最终富文本内容 public string Content { get; set; } = default!; // 单独接收编辑器上传的图片 public IFormFile? Image { get; set; } }
2. 编写Razor页面与页面模型
页面模型(Create.cshtml.cs)
public class CreateModel : PageModel { private readonly IFileUploader _fileUploader; private readonly IPostService _postService; public CreateModel(IFileUploader fileUploader, IPostService postService) { _fileUploader = fileUploader; _postService = postService; } [BindProperty] public PostCreateDto PostDto { get; set; } = default!; // 处理文章发布 public async Task<IActionResult> OnPostAsync() { if (!ModelState.IsValid) return Page(); await _postService.CreatePostAsync(PostDto); return RedirectToPage("./Index"); } // 处理编辑器图片上传请求 public IActionResult OnPostUploadImage() { var imageFile = Request.Form.Files["image"]; if (imageFile == null || imageFile.Length == 0) { return BadRequest("未选择图片"); } // 上传到文章图片目录,用日期作为路径标识 var imageUrl = _fileUploader.Upload(imageFile, $"Post:{DateTime.Now:yyyyMMdd}"); // 返回可直接访问的图片URL return new JsonResult(new { url = $"/UploadedFiles/PostPictures/{imageUrl}".Replace("//", "/") }); } }
Razor页面(Create.cshtml)
@page @model CreateModel <h1>发布文章</h1> <form method="post" enctype="multipart/form-data"> <div class="form-group"> <label asp-for="PostDto.Title"></label> <input asp-for="PostDto.Title" class="form-control" /> <span asp-validation-for="PostDto.Title" class="text-danger"></span> </div> <div class="form-group mt-3"> <label>文章内容</label> <!-- 可编辑富文本区域 --> <div id="editor" contenteditable="true" class="border p-3" style="min-height:300px;"></div> <!-- 隐藏域存储最终富文本内容,用于表单提交 --> <input type="hidden" asp-for="PostDto.Content" id="hiddenContent" /> </div> <!-- 图片上传触发按钮 --> <div class="mt-3"> <input type="file" id="imageUpload" accept="image/*" style="display:none;" /> <button type="button" class="btn btn-secondary" onclick="document.getElementById('imageUpload').click();">插入图片</button> </div> <button type="submit" class="btn btn-primary mt-3">发布文章</button> </form> <script> // 图片上传逻辑 document.getElementById('imageUpload').addEventListener('change', async function(e) { const file = e.target.files[0]; if (!file) return; const formData = new FormData(); formData.append('image', file); const response = await fetch('/Posts/Create?handler=UploadImage', { method: 'POST', headers: { 'RequestVerificationToken': document.querySelector('input[name="__RequestVerificationToken"]').value }, body: formData }); const result = await response.json(); if (response.ok) { // 创建图片标签并插入到编辑器光标位置 const img = document.createElement('img'); img.src = result.url; img.style.maxWidth = '100%'; const editor = document.getElementById('editor'); const selection = window.getSelection(); const range = selection.getRangeAt(0); range.deleteContents(); range.insertNode(img); // 将光标移到图片后方 const newRange = document.createRange(); newRange.setStartAfter(img); newRange.collapse(true); selection.removeAllRanges(); selection.addRange(newRange); } else { alert('图片上传失败:' + result.message); } }); // 提交表单前同步编辑器内容到隐藏域 document.querySelector('form').addEventListener('submit', function() { document.getElementById('hiddenContent').value = document.getElementById('editor').innerHTML; }); </script>
3. 扩展文件上传服务
修改上传服务,支持文章图片的路径区分:
public string Upload(IFormFile file, string path) { string baseDirectory; // 根据路径前缀区分存储目录 if (path.StartsWith("Author:")) { baseDirectory = Path.Combine(_webHostEnvironment.WebRootPath, "UploadedFiles", "AuthorPictures", path.Replace("Author:", "")); } else if (path.StartsWith("Post:")) { baseDirectory = Path.Combine(_webHostEnvironment.WebRootPath, "UploadedFiles", "PostPictures", path.Replace("Post:", "")); } else { baseDirectory = Path.Combine(_webHostEnvironment.WebRootPath, "UploadedFiles", "Others", path); } if (!Directory.Exists(baseDirectory)) Directory.CreateDirectory(baseDirectory); var filename = ToFileName(DateTime.Now) + "-" + file.FileName; var filePath = Path.Combine(baseDirectory, filename); using var output = File.Create(filePath); file.CopyTo(output); // 返回适配Web的相对路径 return $"{path.Replace("Post:", "")}/{filename}".Replace("//", "/"); }
4. 文章展示页面
在文章详情页直接渲染富文本内容:
@page "{id:int}" @model DetailsModel <h1>@Model.Post.Title</h1> <div class="mt-3"> @Html.Raw(Model.Post.Content) </div>
内容的提问来源于stack exchange,提问作者Hamed
相关产品推荐
相关产品推荐

