So I am building a simple android app and I am trying to upload a list of images to a Spring boot server using retrofit. Retrofit is on the kotlin client side. So I first select the images from the phone's gallery and display them on the screen. They are displayed well. Second step is to upload them onto the server and this is where the problem is. First I convert the imageUrls into MultipartBody.Part
objects here:
class FormDataContainer(context: Context): AppContainer {
private val baseUrl = "http://192.168.246.6:8080/pManager/"
private val retrofit = Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create())
.build()
private val retrofitService: PMangerApiService by lazy {
retrofit.create(PMangerApiService::class.java)
}
override val pMangerApiRepository: PMangerApiRepository by lazy {
NetworkPManagerApiRepository(retrofitService)
}
}
Then I invoke the API and pass the images together with some other data in this code:
viewModelScope.launch {
try {
Log.i("UPLOAD_PROPERTY", "Before making network request")
val response = pManagerApiRepository.createProperty(
token = "Bearer ${userDetails.token}",
userId = userDetails.userId.toString(),
property = property,
images = imageParts
)
if (response.isSuccessful) {
val responseBody = response.body()
if (responseBody != null) {
Log.i("CREATE_PROPERTY_RESPONSE", "Status Code: ${responseBody.statusCode}, Message: ${responseBody.message}")
} else {
Log.i("CREATE_PROPERTY_RESPONSE", "Response body is null")
}
} else {
Log.e("CREATE_PROPERTY_RESPONSE", "Unsuccessful response: ${response.code()}")
}
} catch (e: Exception) {
Log.e("CREATE_PROPERTY_EXCEPTION", "Exception: ${e.message}")
}
}
for(part in imageParts) {
Log.i("IMAGE_PARTS", part.toString())
}
I have tried to uplod two images so the two parts
look like this:
okhttp3.MultipartBody$Part@c8aade3
okhttp3.MultipartBody$Part@89963e0
This is the client api endpoint for sending the whole data:
@Multipart
@POST("api/property/userId={id}/create")
suspend fun uploadPropertyDetails(
@Header("Authorization") token: String,
@Path("id") userId: String,
@Part("data") property: Property,
@Part imageFiles: List<MultipartBody.Part>
): Response<PropertyUploadResponse>
And this is how I build retrofit:
class DefaultContainer(context: Context): AppContainer {
private val json = Json { ignoreUnknownKeys = true }
private val baseUrl = "http://192.168.246.6:8080/pManager/"
private val retrofit = Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(json.asConverterFactory("application/json".toMediaType()))
.build()
private val retrofitService: PMangerApiService by lazy {
retrofit.create(PMangerApiService::class.java)
}
override val pMangerApiRepository: PMangerApiRepository by lazy {
NetworkPManagerApiRepository(retrofitService)
}
}
The server side API endpoint is:
@PostMapping(value = "property/userId={userId}/create", consumes = {MediaType.MULTIPART_FORM_DATA_VALUE})
public ResponseEntity<Response> addProperty(@RequestPart(value = "data") @Valid PropertyDTO propertyDTO,@PathVariable int userId,
@RequestParam (value = "imageFiles", required = false) MultipartFile[] imageFiles) throws IOException {
System.out.println("RESPONSE");
System.out.println();
System.out.println(propertyDTO.toString());
System.out.println();
System.out.println("IMAGE FILES:");
for(MultipartFile file : imageFiles) {
System.out.println(file);
}
System.out.println();
return buildResponse("property",propertyService.createProperty(propertyDTO, imageFiles, userId), "Created successfully", HttpStatus.CREATED);
}
I get this error in the server console:
java.lang.NullPointerException: Cannot read the array length because "<local4>" is null
and imageFiles
is null
property
is posted successfully but the images fail at the same time, the error message is indicated above.
However when I call the same endpoint from postman, the images are uploaded successfull. imageFiles
are:
org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile@3eb7d8a7
org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile@3a9c47fc
So basically I am trying to upload form-data to Spring boot server. The data is posted successful if done from postman. However the request fails if done from the mobile app. Actually it's only the photos that are not being sent because the property
is posted successful.
It worked when I updated this line:
imageParts.add(MultipartBody.Part.createFormData(name = "image", file.name, requestFile))
New line:
imageParts.add(MultipartBody.Part.createFormData(name = "imageFiles", file.name, requestFile))
Basically, the name
parameter of createFormData
in kotlin client side should match the value
parameter of @RequestPart
annotation for the images
part (imageFiles
) in java spring boot api endpoint. This is the Spring boot end point:
@PostMapping(value = "property/userId={userId}/create", consumes = {MediaType.MULTIPART_FORM_DATA_VALUE})
public ResponseEntity<Response> addProperty(@RequestPart(value = "data") @Valid PropertyDTO propertyDTO,@PathVariable int userId,
@RequestPart (value = "imageFiles", required = false) MultipartFile[] imageFiles) throws IOException {
System.out.println();
return buildResponse("property",propertyService.createProperty(propertyDTO, imageFiles, userId), "Created successfully", HttpStatus.CREATED);
}
And this is the client side code for creating MultipartBody.Part
objects:
var imageParts = ArrayList<MultipartBody.Part>()
_imagesUiState.value.images.forEach {file ->
Log.i("IMAGE_PATH", file.path)
val imageFile = File(file.path)
val requestFile = imageFile.asRequestBody("image/*".toMediaTypeOrNull())
val imagePart = MultipartBody.Part.createFormData("imageFiles", imageFile.name, requestFile)
imageParts.add(imagePart)
}