ASP.NET Core 5 中如何向指定用户发送通过表单生成的文件
ASP.NET Core 5 MVC 导师向学生发送专属教学大纲图片全流程解决方案
1. 数据库设计方案
- 不需要重构原有用户表,仅扩展关联表即可,禁止直接将图片二进制存入数据库,仅存储文件元数据,实体文件单独存储在服务器独立目录
- 新增专属大纲分发表,核心字段如下:
Id主键自增SenderId导师用户ID,关联用户表主键ReceiverId学生用户ID,关联用户表主键SaveFileName服务器存储的唯一文件名,用Guid生成避免重名OriginalFileName展示用的原始文件名,比如「张三-2024级计算机专业教学大纲.png」FileSize文件大小,单位为字节ContentType文件MIME类型,存image/png/image/jpeg这类标识SendTime发送时间IsRead可选字段,标记学生是否已查看该大纲
- 实体文件存储路径建议放在项目
wwwroot目录之外,避免未授权用户直接通过静态文件地址访问
2. 导师端上传/生成文件功能实现
- 若为导师手动上传大纲,提交表单的ViewModel新增
IFormFile类型字段接收图片文件;若为表单自动生成图片,用SixLabors.ImageSharp开源库动态绘制内容生成图片流即可,跨平台兼容性优于系统自带的System.Drawing - 上传核心代码示例:
[HttpPost] [Authorize(Roles = "导师")] public async Task<IActionResult> SendOutline(SendOutlineViewModel model) { if (!ModelState.IsValid) return View(model); // 校验目标学生是否存在 var student = await _context.Users.FirstOrDefaultAsync(u => u.Id == model.StudentId && u.UserType == 0); if (student == null) { ModelState.AddModelError("", "指定学生不存在"); return View(model); } // 校验文件格式、大小 var allowedExt = new[] { ".png", ".jpg", ".jpeg" }; var ext = Path.GetExtension(model.OutlineFile.FileName).ToLower(); if (!allowedExt.Contains(ext) || model.OutlineFile.Length > 2 * 1024 * 1024) { ModelState.AddModelError("", "仅支持2M以内的png/jpg格式图片"); return View(model); } // 生成唯一存储文件名 var saveName = $"{Guid.NewGuid()}{ext}"; var savePath = Path.Combine(_webHostEnvironment.ContentRootPath, "OutlineFiles", saveName); // 写入文件到磁盘 await using var stream = new FileStream(savePath, FileMode.Create); await model.OutlineFile.CopyToAsync(stream); // 写入数据库记录 var fileRecord = new OutlineFileRecord { SenderId = User.FindFirstValue(ClaimTypes.NameIdentifier), ReceiverId = model.StudentId, SaveFileName = saveName, OriginalFileName = $"{student.Name}-教学大纲{ext}", FileSize = model.OutlineFile.Length, ContentType = model.OutlineFile.ContentType, SendTime = DateTime.Now }; _context.OutlineFileRecords.Add(fileRecord); await _context.SaveChangesAsync(); return RedirectToAction("SendHistory"); }
3. 学生端下载/预览功能实现
- 所有文件访问必须走权限校验接口,禁止直接暴露磁盘存储路径
- 访问文件核心代码示例:
[Authorize] public async Task<IActionResult> GetOutline(int fileId, bool isDownload = false) { var currentUserId = User.FindFirstValue(ClaimTypes.NameIdentifier); // 校验文件归属权限 var fileRecord = await _context.OutlineFileRecords .FirstOrDefaultAsync(f => f.Id == fileId && f.ReceiverId == currentUserId); if (fileRecord == null) return Forbid(); var filePath = Path.Combine(_webHostEnvironment.ContentRootPath, "OutlineFiles", fileRecord.SaveFileName); if (!System.IO.File.Exists(filePath)) return NotFound("文件已被删除"); var fileStream = System.IO.File.OpenRead(filePath); // 预览直接返回文件流,下载指定文件名即可 return isDownload ? File(fileStream, fileRecord.ContentType, fileRecord.OriginalFileName) : File(fileStream, fileRecord.ContentType); }
- 前端展示图片直接调用预览接口即可:
<img src="/Outline/GetOutline?fileId=@item.Id" alt="教学大纲"> - 下载按钮调用带下载参数的接口即可:
<a href="/Outline/GetOutline?fileId=@item.Id&isDownload=true" target="_blank">下载大纲</a>
4. 额外优化建议
- 可新增发送成功后给学生推送站内信的逻辑,避免学生错过通知
- 定期清理已过期的大纲文件,降低服务器存储占用
- 若用户量较大,可将文件存储替换为云对象存储,逻辑仅需修改文件读写部分即可,数据库结构不需要调整
内容的提问来源于stack exchange,提问作者Pezhvak
相关产品推荐
相关产品推荐

