Android Application (Kotlin) to C# API using swagger [Uploading an Image using MultiBody.Part to IFormFile]

33 Views Asked by At

I am currently experiencing a problem where in I can't upload an image with other data from Kotlin to C# API

This is my C# Controller:

[HttpPost("CreateFreelancer")]
[ProducesResponseType(204)]
[ProducesResponseType(400)]
public IActionResult CreateFreelancer([FromForm] int userId, IFormFile imageFile,  FreelancerDTO freelancerDTO)
{

    if (freelancerDTO == null)
        return BadRequest(ModelState);

    var freelancer = _freelancerRepository.GetFreelancers()
        .Where(b => b.freelancerId == freelancerDTO.freelancerId)
        .FirstOrDefault();



    var userEmail = _userRepository.GetUser(userId);


    if (freelancerDTO.ImageFile != null)
    {
        var fileResult = _freelancerRepository.SaveFreelancers(imageFile, userEmail.email);
        freelancerDTO.governmentId = fileResult.Item2.ToString(); // getting name of image

    }

    if (freelancer != null)
    {
        ModelState.AddModelError("", "Freelancer already exists");
        return StatusCode(422, ModelState);
    }

    if (!ModelState.IsValid)
    {
        return BadRequest(ModelState);
    }

    var freelancerMap = _mapper.Map<Freelancer>(freelancerDTO);
    freelancerMap.User = _userRepository.GetUser(userId);
    var productResult = _freelancerRepository.CreateFreelancer(freelancerMap);

    if (!productResult)
    {
        ModelState.AddModelError("", "Something went wrong while saving");
        return StatusCode(500, ModelState);
    }

    return Ok("Successfully Created");
}

and This is my image uploader which is in a repository in C#:

public Tuple<int, string> SaveFreelancers (IFormFile imageFile, string uEmail)
{
    try
    {

        var contentPath = this._environment.ContentRootPath;
        var path = Path.Combine(contentPath, "this\\is\\a\\local\\path");
        if (!Directory.Exists(path))
        {
            Directory.CreateDirectory(path);
        }


        var ext = Path.GetExtension(imageFile.FileName);
        var allowedExtensions = new string[] { ".jpg", ".png", ".jpeg", ".pdf" };
        if (!allowedExtensions.Contains(ext))
        {
            string msg = string.Format("Only {0} extensions are allowed", string.Join(",", allowedExtensions));
            return new Tuple<int, string>(0, msg);
        }

        var newFileName = uEmail + ext;
        var fileWithPath = Path.Combine(path, newFileName);
        var stream = new FileStream(fileWithPath, FileMode.Create);
        imageFile.CopyTo(stream);
        stream.Close();
        return new Tuple<int, string>(1, newFileName);
    }
    catch (Exception ex)
    {
        return new Tuple<int, string>(0, "Error has occured");
    }
}

This is my service to input the data in Kotlin

val freelancerDTO = FreelancerDTO(
    userId = userId,
    idType = idType,
    verificationStatus = false,
    totalIncome =  0
)
val gson = Gson()
val freelancerDTOJson = gson.toJson(freelancerDTO)
val freelancerDTORequestBody = RequestBody.create("application/json".toMediaTypeOrNull(), freelancerDTOJson)
val freelancerDTOPart = MultipartBody.Part.createFormData("freelancerDTO", freelancerDTOJson, freelancerDTORequestBody)

val parcelFileDescriptor = contentResolver.openFileDescriptor(
    selectedImageUri!!, "r", null
) ?: return

val inputStream = FileInputStream(parcelFileDescriptor.fileDescriptor)
val imageFile = File(cacheDir,contentResolver.getFileName(selectedImageUri!!))
val outputStream = FileOutputStream(imageFile)
inputStream.copyTo(outputStream)
val body = UploadRequestBody(imageFile,"image/*")
val imagePart = MultipartBody.Part.createFormData(
    "imageFile",
    imageFile.name,
    body
)

Log.d("TEST", "${call.toString()}")
service.createFreelancer(userId, imagePart, freelancerDTOPart)
.enqueue(object : Callback<Freelancer> {
    override fun onResponse(call: Call<Freelancer>, response: Response<Freelancer>) {

        if (response.isSuccessful) {

        } else {
            // Handle the error response
            val errorBody = response.errorBody()?.string()
            Log.d("Upload", "Error response: $errorBody")
        }
    }

    override fun onFailure(call: Call<Freelancer>, t: Throwable) {
        // Handle the failure
        Log.d("Upload", "File and attributes upload failed: ${t.message}")
        if (t is IOException) {
            Log.d("Upload", "Error response: ${t.localizedMessage}")
        }
    }
})

LASTLY

This is my API Interface in Kotlin

@Multipart
@POST("api/Freelancer/CreateFreelancer")
fun createFreelancer(
    @Part("userId") userId: Int,
    @Part imageFile: MultipartBody.Part,
    @Part("freelancerDTO") freelancerDTO: MultipartBody.Part
): Call<Freelancer>

I tried doing different methods but in the end it just saves the userId in the database (which is a foreign key and is part of the parameter). It doesnt save the Image and other details. The outcome would be the image will be saved in the local folder and the data will also be saved in the database SQL.

0

There are 0 best solutions below