Итак, я создаю простое приложение для Android и пытаюсь загрузить список изображений на загрузочный сервер Spring с помощью модернизации. Модернизация выполняется на стороне клиента Kotlin. Поэтому я сначала выбираю изображения из галереи телефона и показываю их на экране. Они показаны хорошо. Второй шаг — загрузить их на сервер, и именно здесь возникает проблема. Сначала я конвертирую imageUrls в объекты MultipartBody.Part
здесь:
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)
}
}
Затем я вызываю API и передаю изображения вместе с некоторыми другими данными в этот код:
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())
}
Я попытался загрузить два изображения, поэтому они parts
выглядят так:
okhttp3.MultipartBody$Part@c8aade3
okhttp3.MultipartBody$Part@89963e0
Это конечная точка клиентского API для отправки всех данных:
@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>
И вот как я строю модернизацию:
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)
}
}
Конечная точка API на стороне сервера:
@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);
}
Я получаю эту ошибку в консоли сервера:
java.lang.NullPointerException: Cannot read the array length because "<local4>" is null
и imageFiles
равен нулю
property
публикуется успешно, но изображения при этом не работают, сообщение об ошибке указано выше.
Однако когда я вызываю ту же конечную точку от почтальона, изображения загружаются успешно. imageFiles
это:
org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile@3eb7d8a7
org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile@3a9c47fc
Итак, в основном я пытаюсь загрузить данные формы на загрузочный сервер Spring. Данные публикуются успешно, если они сделаны почтальоном. Однако запрос не выполняется, если он выполнен из мобильного приложения. На самом деле не отправляются только фотографии, потому что property
успешно опубликован.
Это сработало, когда я обновил эту строку:
imageParts.add(MultipartBody.Part.createFormData(name = "image", file.name, requestFile))
Новая линия:
imageParts.add(MultipartBody.Part.createFormData(name = "imageFiles", file.name, requestFile))
По сути, параметр name
createFormData
на стороне клиента Kotlin должен соответствовать параметру value
аннотации @RequestPart
для части images
(imageFiles
) в конечной точке Java Spring Boot API. Это конечная точка загрузки Spring:
@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);
}
А это клиентский код для создания объектов MultipartBody.Part
:
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)
}