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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 13:56:14