Sending list of files with additional data using HttpClient in .NET Core

Viewed 882

I am using a micro service architecture, in API gateway, I get a request and I would like to send that request which contains List<PictureItemDto> pictures to Attachment Microservice and store picture information.

I have these model classes in both the Gateway and the Attachment MicroService:

public class PictureDto
{
    public List<PictureItemDto> pictures { get; set; }
}

public class PictureItemDto
{
    public string seoFilename { get; set; }
    public string altAttribute { get; set; }
    public string titleAttribute { get; set; } 
    public IFormFile file { get; set; }
}

And this is how I send data from API gateway to Attachment MicroService:

public async Task<bool> Create(List<PictureItemDto> postPictures)
{
    MultipartFormDataContent formDataContent = new MultipartFormDataContent();

    for (int i = 0; i < postPictures.Count; i++)
    {
        var item = postPictures[i];
        formDataContent.Add(new StringContent(item.seoFilename), $"pictures[{i}].seoFilename");
        formDataContent.Add(new StringContent(item.altAttribute), $"pictures[{i}].altAttribute");
        formDataContent.Add(new StringContent(item.titleAttribute), $"pictures[{i}].titleAttribute");

        StreamContent fileContent = new(item.file.OpenReadStream())
                      {
                          Headers =
                              {
                                  ContentLength =  item.file.Length,
                                  ContentType = new MediaTypeHeaderValue(item.file.ContentType)
                              }
                      };

        formDataContent.Add(fileContent, $"pictures[{i}].file", item.file.FileName);
    }

    var result = await _httpClient.PostAsync($"{_urlsOptions.AttachmentUrl}", formDataContent);

    return (result.StatusCode == HttpStatusCode.OK) ? true : false;
}

My problem is that the data is received null in CreatePostPictures action(this action is in attachment microservice) :

[HttpPost("create")]
public async Task<IActionResult> CreatePostPictures([FromForm]PictureDto dto)
{
    return Ok();
}

This is curl that I am sending :

curl --location --request POST 'http://localhost:80/api/Post?api-version=1.0' \
--header 'accept: */*' \
--header 'api-version: 1.0' \
--header 'Authorization: Bearer eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJrYXZlaEBnbWFpbC5jb20iLCJqdGkiOiJlYjhkZmIyMy0wYTAxLTQzOWEtOWM1NS01Y2NmY2I2NmY3YzgiLCJ1bmlxdWVfbmFtZSI6ImthdmVoQGd' \
--form 'postPictures[0].titleAttribute="titleAttribute"' \
--form 'postPictures[0].seoFilename="seoFilename"' \
--form 'postPictures[0].altAttribute="altAttribute"' \
--form 'postPictures[0].File=@"/F:/Photos/WallPapers Pack 2011/Series 2/1024x768/WP2011.2 1024x768 (8).jpg"' \
--form 'postPictures[1].titleAttribute="titleAttribute1"' \
--form 'postPictures[1].seoFilename="seoFilename1"' \
--form 'postPictures[1].altAttribute="altAttribute1"'
--form 'postPictures[1].File=@"/F:/Photos/WallPapers Pack 2011/Series 2/1024x768/WP2011.2 1024x768 (6).jpg"' \
1 Answers

I finally solved it by converting IFormFile to Base64String and convert it back to IFormFile :

First I Add two new Properties to PictureItemDto class :

public class PictureItemDto
{
    public string seoFilename { get; set; }
    public string altAttribute { get; set; }
    public string titleAttribute { get; set; } 
    public IFormFile file { get; set; }
    
    // New
    public string Base64File { get; set; }
    public string ContentType { get; set; }
}

Then In API Gateway The File that is received from Request should be converted to Base64String

  public async Task<bool> Create(List<PictureItemDto> postPictures)
        {
            var i = 0;
            foreach (var file in postPictures.Select(f => f.File))
            {
                if (file.Length > 0)
                {
                    using MemoryStream ms = new();
                    file.CopyTo(ms);
                    var fileBytes = ms.ToArray();
                    postPictures[i].Base64File = Convert.ToBase64String(fileBytes);
                    postPictures[i].ContentType = file.ContentType;
                    postPictures[i].File = null;
                    i++;
                }
            }

            JsonContent content = JsonContent.Create(postPictures);

            HttpResponseMessage response = await _httpClient.PostAsync($"{_urlsOptions.AttachmentUrl}{UrlsOptions.Attachment.EntityContentController.CreatePostPictures()}", content);
            return (response.StatusCode == HttpStatusCode.OK) ? true : false;
        }

And In Attachment Microservice I receive data and convert it back to IFormFile :

  [HttpPost("create")]
  public async Task<IActionResult> CreatePostPictures([FromBody] List<PictureItemDto> models)
  {
            List<IFormFile> formFiles = new List<IFormFile>();
            foreach (var item in models)
            {
                byte[] bytes = Convert.FromBase64String(item.Base64File);
                MemoryStream stream = new(bytes);

                //item.File = new FormFile(stream, 0, bytes.Length, item.SeoFilename, item.SeoFilename);
                item.File = new FormFile(stream, 0, stream.Length, null, item.SeoFilename)
                {
                    Headers = new HeaderDictionary(),
                    ContentType = item.ContentType
                };
            }
   }
Related