使用JSON的ASP.NET Core API进行多部分/form-data图片上传

5
如何在单个POST请求中同时上传图片和JSON数据?(使用multipart方式) 我有一个表单,其中一些数据被转换成了JSON格式,用户可以添加0到6张照片并将其提交到API。请问如何实现这个功能? 编辑: 以下是我的代码,感谢您的帮助:
    // POST api/<controller>
    [HttpPost, Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
    public IActionResult Post(ViewModel vm)
    {
        IActionResult response = Unauthorized();

        var data = vm.FamilleProduit;
        var reforigine = vm.RefOrigine;

        if (vm.Images != null)
        {
            foreach (var image in vm.Images)
            {
                byte[] fileData = null;

                // read file to byte array
                using (var binaryReader = new BinaryReader(image.OpenReadStream()))
                {
                    fileData = binaryReader.ReadBytes((int)image.Length);
                }
            }
        }
        return response;
    }

    public class ViewModel
    {
        public string FamilleProduit { get; set; }
        public string RefOrigine { get; set; }
        public List<IFormFile> Images { get; set; }
    }

我正在使用Postman进行测试,我用“multipart/form-data” POST了2个文本(FamilleProduit和RefOrigine)和2个文件(2个图像)。我可以完美地获取这2个文本,但是每次图像字段都为空。
谢谢, Tristan。

“最好”的选择有点主观,但其中一个选项是将所有内容都作为JSON传输,图像可以编码为base64并放置在JSON属性中。 - ADyson
将所有内容作为JSON发送可能是一个选项,但我与一位移动开发人员合作,他使用多部分。 - Tristan Sébillet
这要看你能否修改API以接受任何特定格式,还要看其他开发者是否愿意修改他们的代码以符合所需格式。 - ADyson
我会看一下。谢谢。 - Tristan Sébillet
1个回答

5

您可以使用内置的IFormFile类轻松处理文件上传。如果要与JSON一起使用,可以创建自定义模型绑定器并将其与DTO对象结合使用:

public class ViewModel
{
    [ModelBinder(BinderType = typeof(FormDataJsonBinder))]
    public DataModel Data { get;set; }

    public List<IFormFile> Images { get; set; }
}

public class FormDataJsonBinder : IModelBinder
{
    public Task BindModelAsync(ModelBindingContext bindingContext)
    {
        if(bindingContext == null)
        {
            throw new ArgumentNullException(nameof(bindingContext));
        }    

        string fieldName = bindingContext.FieldName;
        var valueProviderResult = bindingContext.ValueProvider.GetValue(fieldName);

        if(valueProviderResult == ValueProviderResult.None)
        {
            return Task.CompletedTask;
        }
        else
        {
            bindingContext.ModelState.SetModelValue(fieldName, valueProviderResult);
        }    

        string value = valueProviderResult.FirstValue;
        if(string.IsNullOrEmpty(value))
        {
            return Task.CompletedTask;
        }

        try
        {                
            object result = JsonConvert.DeserializeObject(value, bindingContext.ModelType);
            bindingContext.Result = ModelBindingResult.Success(result);
        }
        catch(JsonException)
        {
            bindingContext.Result = ModelBindingResult.Failed();
        }

        return Task.CompletedTask;
    }
}

然后您可以在控制器中使用它:
[HttpPost]
public IActionResult Create(ViewModel vm)
{
    var data = vm.Data;

    if (vm.Images != null)
    {
        foreach(var image in vm.Images)
        {
            byte[] fileData = null;

            // read file to byte array
            using (var binaryReader = new BinaryReader(image.OpenReadStream()))
            {
                fileData = binaryReader.ReadBytes((int)image.Length);
            }
        }
    }
}

我使用了你的代码,成功获取了数据,但是在Postman测试时,“Images”为空。你知道这可能是由什么原因引起的吗?如果需要,我可以发布我的代码。 - Tristan Sébillet
1
@TristanSébillet 表单数据中的键名应该与属性名称匹配。 - Alex Riabov
哦,我累坏了,而且我错过了它,谢谢你的提醒!祝你有美好的一天。它完美地运行 :) - Tristan Sébillet
@TristanSébillet,你能告诉我你如何使用Postman进行测试吗?我也在尝试同样的操作,但每次都会收到“输入无效”的消息。我已经检查了键名。谢谢! - Johna
没事了,postman 上有一个头部 [{"key":"Content-Type","value":"application/x-www-form-urlencoded"}],是我的错误。 - Johna
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接