5

How to POST both images and JSON at the same time in a single POST ? (using multipart) I have a form with some data that i put into JSON and the users can add 0 to 6 photos and submit it to the API.

Can someone explain me how to do it ?

Edit : Here is my code thanks to your help :

    // 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; }
    }

I'm testing with Postman and i POST 2 text (FamilleProduit & RefOrigine) and 2 files (2 images) with "multipart/form-data". I get the 2 texts perfectly but Images field is null everytime.

Thanks, Tristan.

  • "best" is a bit subjective, but one option is to just transmit everything as JSON, the images can be encoded as base64 and placed into properties within the JSON – ADyson Jul 31 '18 at 13:59
  • Sending everything as JSON could be an option but i work with a mobile developer and he use multipart. – Tristan Sébillet Jul 31 '18 at 14:04
  • well it depends if you can modify the API to accept any particular format, and whether the other developer is happy to modify their code to comply with the required format. – ADyson Jul 31 '18 at 14:06
  • I'll have a look into it. Thanks – Tristan Sébillet Jul 31 '18 at 14:17

1 Answers1

5

You can use built-in class IFormFile to easily work with file upload. To use it together with JSON you may create custom model binder and combine it together in DTO object:

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;
    }
}

Then you can work with it in your controller:

[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);
            }
        }
    }
}
Alex Riabov
  • 8,655
  • 5
  • 47
  • 48
  • I tried with your code, i retrieve the Data but the "Images" is null when i test with postman. Do you know where it can come from ? i'll post my code if needed. – Tristan Sébillet Jul 31 '18 at 14:38
  • 1
    @TristanSébillet Keys in form-data should match property name – Alex Riabov Jul 31 '18 at 14:43
  • oh f.. I'm tired and i missed it, thanks for the reminder ! have a great day it works perfectly :) – Tristan Sébillet Jul 31 '18 at 14:49
  • @TristanSébillet can you tell me how you test with postman? I'm trying to do the same, but every time I receive "The input was not valid." I already check the keys names. Thanks! – Johna Sep 30 '18 at 22:04
  • Nevermind, there was a header [{"key":"Content-Type","value":"application/x-www-form-urlencoded"}] on postman, my mistake – Johna Sep 30 '18 at 22:27
  • @AlexRiabov can you please provide a code sample about how to call this api via c# as well? I can't get it working – AFgone Jun 04 '20 at 17:44
  • @AFgone you can find it here: https://stackoverflow.com/a/19983672/9608718 – Alex Riabov Jun 05 '20 at 10:04
  • @AlexRiabov but this is just one file. Your sample has list of files and that sample is not working with list of files. How we can send list of files from c# client? – AFgone Jun 05 '20 at 18:51