Детали EXIF ​​потеряны при публикации изображения с использованием Retrofit 2 с MultipartBody в Android? - PullRequest
1 голос
/ 09 июля 2020

Мы захватываем несколько изображений с помощью CameraX, сохраняем их на устройстве и публикуем эти изображения и текстовые данные с помощью MultipartBody.Part и RequestBody. Мы обнаружили, что не получаем данные EXIF ​​на стороне сервера. Это проблема с Retrofit 2 Multipart? Как мы можем получить изображения на стороне сервера, не теряя его данные EXIF?

Вот код, который мы используем

private lateinit var activityCaptureImageBinding: ActivityCaptureImageBinding

private var preview: Preview? = null
private var imageCapture: ImageCapture? = null
private var camera: Camera? = null
private lateinit var outputDirectory: File
private lateinit var cameraExecutor: ExecutorService

private lateinit var fusedLocationClient: FusedLocationProviderClient
private var metadata = ImageCapture.Metadata()

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    activityCaptureImageBinding = DataBindingUtil.setContentView(this@CaptureImageActivity, R.layout.activity_capture_image)
    window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)

    fusedLocationClient = LocationServices.getFusedLocationProviderClient(this)
    if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
        return
    }
    fusedLocationClient.lastLocation
            .addOnSuccessListener { location: Location? ->
                metadata.apply {
                    setLocation(location)
                }
            }

    activityCaptureImageBinding.btnCaptureImage.setOnClickListener {
        takePhoto()
    }

    activityCaptureImageBinding.btnCancel.setOnClickListener {
        val intent = Intent()
        intent.putStringArrayListExtra(AppData.IMAGE_PATH, AppData.capturedCatsList)
        setResult(Activity.RESULT_OK, intent)
        finish()
    }

    outputDirectory = getOutputDirectory()
    cameraExecutor = Executors.newSingleThreadExecutor()
}

private fun startCamera() {
    val cameraProviderFuture = ProcessCameraProvider.getInstance(this)

    cameraProviderFuture.addListener(Runnable {
        val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()

        preview = Preview.Builder()
                .build()

        imageCapture = ImageCapture.Builder()
                .build()

        val cameraSelector = CameraSelector.Builder().requireLensFacing(CameraSelector.LENS_FACING_BACK).build()

        try {
            cameraProvider.unbindAll()

            camera = cameraProvider.bindToLifecycle(
                    this, cameraSelector, preview, imageCapture)
            preview?.setSurfaceProvider(viewFinder.createSurfaceProvider())
        } catch (exc: Exception) {
            showLog(TAG, "Use case binding failed" + exc)
        }

    }, ContextCompat.getMainExecutor(this))
}


private fun takePhoto() {

    val imageCapture = imageCapture ?: return

    val photoFile = File(
            outputDirectory,
            SimpleDateFormat(FILENAME_FORMAT, Locale.getDefault()
            ).format(System.currentTimeMillis()) + ".jpg")

    showLog(TAG, "created file")

    val outputOptions = ImageCapture.OutputFileOptions.Builder(photoFile)
            .setMetadata(metadata)
            .build()

    imageCapture.takePicture(
            outputOptions, ContextCompat.getMainExecutor(this), object : ImageCapture.OnImageSavedCallback {
        override fun onError(exc: ImageCaptureException) {
            showLog(TAG, "Photo capture failed: ${exc.message}")
        }

        override fun onImageSaved(output: ImageCapture.OutputFileResults) {
            showLog(TAG, "on image saved")
            val savedUri = Uri.fromFile(photoFile)
            AppData.capturedCatsList.add(savedUri.toString())
        }
    })
}

Ниже приведен код модернизации

@Multipart
@POST("SimpleEncounterForm")
fun postCatImagesWithData(@Header("Cookie") cookie: String,
                          @Part catImages: List<MultipartBody.Part>,
                          @Part("datetime") dateTime: RequestBody,
                          @Part("lat") lat: RequestBody,
                          @Part("lon") lon: RequestBody,
                          @Part("locationID") locationId: RequestBody,
                          @Part("behavior") catBehaviour: RequestBody): Call<ResponseBody>

Метод, который мы используем для создания MultipartBody.Part из Uri сохраненных изображений

@NonNull
private fun postFiles(fileUri: Uri): MultipartBody.Part {
    val file = FileUtils.getFile(this@DashboardActivity, fileUri)
    val fileReqBody = RequestBody.create(MediaType.parse("image/jpeg"), file)
    return MultipartBody.Part.createFormData("upload", file.name, fileReqBody)
}

Преобразование Uri в файл

public static File getFile(Context context, Uri uri) {
    if (uri != null) {
        String path = getPath(context, uri);
        if (path != null && isLocal(path)) {
            return new File(path);
        }
    }
    return null;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...