ASP.NET Core Web API+Angular13如何实现图片上传存储到文件目录
图片上传存储完整落地实现
首先明确:你当前Angular端直接提交JSON的逻辑无法传输文件,文件类型数据不能放在普通JSON payload中传输,必须使用multipart/form-data格式的FormData对象提交,以下是前后端全流程可直接复用的实现代码。
一、前端Angular侧修改
1. 完善TS逻辑
先补全文件选择、FormData组装逻辑,替换原有submit方法:
// 新增属性存储选中的文件对象 selectedFile: File | null = null; imageSrc: string | ArrayBuffer | null = null; // 补全你已写的文件选择回调 onFileChange(event: Event) { const input = event.target as HTMLInputElement; if (input.files && input.files[0]) { this.selectedFile = input.files[0]; // 本地预览逻辑 const reader = new FileReader(); reader.onload = e => { this.imageSrc = reader.result; }; reader.readAsDataURL(this.selectedFile); } } // 重写提交方法,用FormData传参 submit() { if (!this.myForm.valid) return; const formData = new FormData(); // 塞入普通表单字段 formData.append('ProductName', this.myForm.get('ProductName')?.value); formData.append('ProductDescription', this.myForm.get('ProductDescription')?.value); formData.append('price', this.myForm.get('price')?.value); formData.append('created', this.myForm.get('created')?.value); formData.append('ProductCatID', this.myForm.get('ProductCatID')?.value); // 塞入选中的图片文件,字段名和后端接收参数保持一致 if (this.selectedFile) { formData.append('ProductImage', this.selectedFile, this.selectedFile.name); } this.http.post('https://localhost:5001/api/Products', formData) .subscribe(res => { console.log(res); alert('上传成功'); // 提交成功后重置表单 this.myForm.reset(); this.selectedFile = null; this.imageSrc = null; }) }
2. 表单小调整
移除文件输入框上绑定的formControlName="ImageUrl",该控件存储的是文件对象而非字符串路径,绑定到字符串类型的表单控件会触发校验报错,同时加格式限制:
<input type="file" name="Image" id="Image" class="form-control mt-3 mb-2" (change)="onFileChange($event)" accept="image/*">
二、后端.NET Web API侧修改
1. 配置静态文件访问与存储目录
在Program.cs(.NET 6+)中添加配置,自动创建图片存储目录、开启静态文件访问,保证上传后的图片可以通过URL直接访问:
// 放在var app = builder.Build(); 之后,路由配置之前 // 自动创建图片存储目录:wwwroot/upload/products var uploadPath = Path.Combine(builder.Environment.WebRootPath, "upload/products"); if (!Directory.Exists(uploadPath)) { Directory.CreateDirectory(uploadPath); } // 开启静态文件中间件 app.UseStaticFiles();
注意:部署时需要给应用运行账号分配upload目录的读写权限,不要给该目录开放脚本执行权限,避免安全风险。
2. 修改CreateProductDto
移除原有的ImageUrl字段,新增IFormFile类型属性接收上传的文件:
public class CreateProductDto { [Required] public string ProductName { get; set; } [Required] public string ProductDescription { get; set; } [Required] public decimal price { get; set; } public DateTime created { get; set; } [Required] public int ProductCatID { get; set; } // 接收前端传的图片文件,字段名和FormData中append的名称一致 public IFormFile? ProductImage { get; set; } }
3. 修改控制器Post接口,添加文件处理逻辑
先注入IWebHostEnvironment获取网站根目录路径,再补充文件校验、存储、访问路径生成逻辑:
// 注入环境对象,原有已注入的mapper、数据服务保留 private readonly IWebHostEnvironment _webHostEnv; private readonly IMapper _mapper; private readonly ISqlService _SqlService; public ProductsController(IMapper mapper, ISqlService sqlService, IWebHostEnvironment webHostEnv) { _mapper = mapper; _SqlService = sqlService; _webHostEnv = webHostEnv; } [HttpPost] // 加[FromForm]标记,指定接收FormData格式参数 public ActionResult<ProductDto> CreateProduct([FromForm]CreateProductDto pro) { try { var productEntity = _mapper.Map<Product>(pro); // 图片处理逻辑 if (pro.ProductImage != null && pro.ProductImage.Length > 0) { // 校验文件格式,仅允许常见图片类型 var allowedExts = new[] { ".jpg", ".jpeg", ".png", ".webp" }; var fileExt = Path.GetExtension(pro.ProductImage.FileName).ToLowerInvariant(); if (!allowedExts.Contains(fileExt)) { return BadRequest("仅支持jpg、jpeg、png、webp格式的图片"); } // 校验文件大小,单图最大限制5MB if (pro.ProductImage.Length > 5 * 1024 * 1024) { return BadRequest("图片大小不能超过5MB"); } // 生成唯一文件名,避免重名覆盖 var uniqueFileName = $"{Guid.NewGuid()}{fileExt}"; // 拼接文件存储的物理路径 var fileSavePath = Path.Combine(_webHostEnv.WebRootPath, "upload/products", uniqueFileName); // 保存文件到服务器目录 using (var stream = new FileStream(fileSavePath, FileMode.Create)) { pro.ProductImage.CopyTo(stream); } // 生成图片可访问的相对路径,赋值给实体字段准备入库 productEntity.ImageUrl = $"/upload/products/{uniqueFileName}"; } else { // 按业务要求处理,图片必填就返回错误,非必填可以设置默认图路径 return BadRequest("请上传产品图片"); } var newProduct = _SqlService.AddProduct(productEntity); var productForReturn = _mapper.Map<ProductDto>(newProduct); return CreatedAtRoute("GetProduct", new { id = productForReturn.ProId }, productForReturn); } catch(Exception ex) { return StatusCode(500, $"服务器内部错误: {ex.Message}"); } }
三、验证要点
全部修改完成后按以下顺序验证:
- 提交表单时F12查看请求,Content-Type为
multipart/form-data,请求载荷中可以看到普通字段和文件二进制内容 - 服务器
wwwroot/upload/products目录下能看到上传后重命名的图片文件 - 数据库Product表的ImageUrl字段存储了正确的相对路径
- 直接访问
域名+ImageUrl(比如https://localhost:5001/upload/products/xxx.jpg)可以正常打开图片 - 前端拿到产品数据后,拼接后端域名即可正常渲染产品图片
内容的提问来源于stack exchange,提问作者Malshan Hansaka
相关产品推荐
相关产品推荐

