所以我正在构建一个简单的 Android 应用程序,并尝试使用改造将图像列表上传到 Spring boot 服务器。 Retrofit 位于 kotlin 客户端。因此,我首先从手机图库中选择图像并将其显示在屏幕上。它们显示得很好。第二步是将它们上传到服务器上,这就是问题所在。首先,我将 imageUrls 转换为
MultipartBody.Part
对象:
var imageParts = ArrayList<MultipartBody.Part>()
_imagesUiState.value.images.forEach {imageUrl ->
val inputStream = context.contentResolver.openInputStream(imageUrl)
try {
inputStream?.let {
val extension = context.contentResolver.getType(imageUrl)?.let {
MimeTypeMap.getSingleton().getExtensionFromMimeType(it)
} ?: "jpg"
val file = File(context.cacheDir, "${System.currentTimeMillis()}.$extension")
val outputStream = FileOutputStream(file)
inputStream.copyTo(outputStream)
val requestFile = file.asRequestBody("image/*".toMediaTypeOrNull())
imageParts.add(MultipartBody.Part.createFormData("image", file.name, requestFile))
outputStream.close()
inputStream.close()
}
} catch (e: Exception) {
Log.e("FILE_READ_ERROR", "Error reading file: ${e.message}")
}
}
然后我调用 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 boot 服务器。如果由邮递员完成,则数据发布成功。但是,如果通过移动应用程序完成请求,则会失败。其实只是照片没有发送,因为
property
已经发布成功了。
当我更新此行时它起作用了:
imageParts.add(MultipartBody.Part.createFormData(name = "image", file.name, requestFile))
新线路:
imageParts.add(MultipartBody.Part.createFormData(name = "imageFiles", file.name, requestFile))
基本上,kotlin 客户端中
name
的 createFormData
参数应该与 java spring boot api 端点中 value
部分 (@RequestPart
) 的 images
注释的 imageFiles
参数相匹配。这是 Spring boot 的终点:
@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 {imageUrl ->
Log.i("IMAGE_PATH", imageUrl.path)
val imageFile = File(imageUrl.path)
val requestFile = imageFile.asRequestBody("image/*".toMediaTypeOrNull())
val imagePart = MultipartBody.Part.createFormData(name = "imageFiles", imageFile.name, requestFile)
imageParts.add(imagePart)
}