[Kotlin] 이미지 가져와서 서버에 전달 후 View에 띄우기

Hand·2022년 7월 31일
0

Android

목록 보기
5/17

💡 그냥 갤러리에서 이미지를 가져와서 View에 표시하는 것이 아닌, 서버를 통해 전달받은 url을 이용하여 view에 표시하는 방법을 말합니다.

로직

  • 클라이언트가 갤러리에서 이미지파일을 선택하면, 선택한 이미지파일을 서버에 전달해줍니다.
  • 서버는 이 파일을 S3서버를 통해 URL로 만들어서 다시 클라이언트에게 반환해줍니다.
  • 클라이언트에서는 이 URL을 통해 View에 이미지를 띄우게 됩니다.

Permission

파일을 읽고 쓰기 위해서는 권한을 설정해주는 작업이 필요합니다.

권한 설정을 위해서는 AndroidManifest.xml에 필요한 권한을 설정해준 후, 코드를 통해서 사용자에게 허락을 구하면 됩니다.

AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.hand.portfolian">

    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    ...

권한 설정

// 현재 기기에 설정된 쓰기 권한을 가져오기 위한 변수
var writePermission = ContextCompat.checkSelfPermission(this, android.Manifest.permission.WRITE_EXTERNAL_STORAGE )

// 현재 기기에 설정된 읽기 권한을 가져오기 위한 변수
var readPermission = ContextCompat.checkSelfPermission(this, android.Manifest.permission.READ_EXTERNAL_STORAGE)

// 읽기 권한과 쓰기 권한에 대해서 설정이 되어있지 않다면
if (writePermission == PackageManager.PERMISSION_DENIED || readPermission == PackageManager.PERMISSION_DENIED) {
	// 읽기, 쓰기 권한을 요청합니다.
	ActivityCompat.requestPermissions(
    	this,
        arrayOf(
			android.Manifest.permission.WRITE_EXTERNAL_STORAGE,
            android.Manifest.permission.READ_EXTERNAL_STORAGE
		),
        1
	)
} 
// 위 경우가 아니라면 권한에 대해서 설정이 되어 있으므로
else {
	var state = Environment.getExternalStorageState()
	
    // 갤러리를 열어서 파일을 선택할 수 있도록 합니다.
    if (TextUtils.equals(state, Environment.MEDIA_MOUNTED)) {
		val intent = Intent(Intent.ACTION_PICK)
		intent.type = "image/*"
		getResult.launch(intent)
	}
}

갤러리에서 이미지 파일을 가져오기

위의 코드에서 보면 getResult.lauch(intent) 라는 코드가 보일 것입니다.

이는 intent의 결과를 가져오기 위한 변수를 설정한 것으로

lateinit var getResult: ActivityResultLauncher<Intent>

이렇게 전역으로 변수를 선언해준 후, onCreate() 내에서 사용해주면 됩니다.

여기서 중요한 것은 꼭!!! onCreate() or onStart() 내에서 선언을 해주어야 한다는 것입니다.
registerForActivityResult() 함수의 경우 onResume()에서 선언을 해줄 경우 에러가 발생합니다!

override fun onCreate(savedInstanceState: Bundle?) {
	super.onCreate(savedInstanceState)

	getResult = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
		if (it.resultCode == Activity.RESULT_OK) {
			filePath = getRealPathFromURI(it.data?.data!!)
		}
	}
}

intent의 결과를 받을 수 있는 함수로 it 내에서 Uri형태의 데이터를 받을 수 있습니다.

데이터를 통해 파일경로 알아내기

우리는 여기서 Uri형식의 데이터에서 파일경로를 읽어와서 이 파일경로를 통해서 File형식의 데이터를 가져올 것입니다.

private fun getRealPathFromURI(uri: Uri): String {
	val buildName = Build.MANUFACTURER
	if(buildName.equals("Xiaomi")) {
		return uri.path.toString()
	}

	var columnIndex = 0
	val proj = arrayOf(MediaStore.Images.Media.DATA)
	var cursor = contentResolver.query(uri, proj, null, null, null)
	
    if(cursor!!.moveToFirst()) {
		columnIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA)
	}

	return cursor.getString(columnIndex)
}

만약 샤오미사의 휴대폰일 경우 그냥 경로를 반환해주면 되지만, 그게 아닐 경우 파일을 찾고 경로를 찾아주는 작업이 필요합니다.

파일경로를 통해 파일을 찾아서 서버에 전달하기

이 글을 찾은 분들의 경우 서버에서 이미지파일을 전달받는 api를 만들어서 알려주었을 것입니다.
이미지 파일의 경우 Multipart에 담아서 전달을 해주어야 합니다.

@Multipart
@PATCH("url 경로")
fun modifyCustomProfile(
	@Part photo: MultipartBody.Part
) : Call<ResponseData>

이렇게 설정해줘서 Retrofit2에서 사용하면 Mutipart에 담아서 전달해줄 수 있습니다..

이제 가져온 파일을 담고 보내줄 일만 남았습니다.

Uri를 통해 알아낸 파일경로를 통해서 파일을 가져온 후,
이를 다시 Multipart에 담아서 보내주어야 합니다.

val file = File(filePath)
val requestFile = RequestBody.create("image/*".toMediaTypeOrNull(), file)
val body: MultipartBody.Part = MultipartBody.Part.createFormData("photo", "photo", requestFile)

Mutipart에 담은 파일을 RequestData에 담아서 전송하면
서버에서는 이 파일을 토대로 url 링크를 만들 것이고,
이 url 링크를 통해서 view에 띄우는 작업을 하면 완료입니다!

Url 링크를 통해서 View에 띄우기

위의 복잡했던 작업과 달리 이는 아~주 간단했습니다.

바로 Glide라는 라이브러리를 사용해주면 엄청 간단히 링크를 통해서 이미지를 볼 수 있습니다.
Glide를 사용해주기 위해선 먼저 gradle에서 설정해줄 필요가 있습니다.

build.gradle(app)

dependencies {
	implementation 'com.github.bumptech.glide:glide:4.11.0'
    annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'
}

그 후 view를 binding한 후, 링크를 걸어주면 됩니다.

val profile = binding.cvProfile

// 링크가 없는 경우 기본 이미지를 보여주기 위함
if(profileURL.isNullOrEmpty()) {
	profile.setImageDrawable(getDrawable(R.drawable.avatar_1_raster))
} 
// 링크가 있는 경우 링크에서 이미지를 가져와서 보여준다.
else {
	Glide.with(this)
    	.load(profileURL)
        .into(profile)
}

profileUrl은 위의 과정을 통해 ResponseData로 서버에서 전달받은 링크입니다.
링크가 없을 경우 보여줄 이미지에 대해서는 res -> drawable 내에 이미지 파일을 넣고 가져와서 쓰면 됩니다.

이렇게 길고 길었던 이미지 파일을 전송한 후 링크를 받아서 view에 표시하는 작업이 완료되었습니다!!

코드 전문을 보고싶으신 분은 아래의 링크를 통해 확인하시면 됩니다.

bulid.gradle
https://github.com/PORTFOLIAN/Portfolian-Android/blob/main/Portfolian/app/build.gradle

위의 코드를 사용한 Activity
https://github.com/PORTFOLIAN/Portfolian-Android/blob/main/Portfolian/app/src/main/java/com/example/portfolian/view/main/user/ProfileModifyActivity.kt

profile
화이팅!

0개의 댓글