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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.03 03:57:36