본문 바로가기

개발 노트/Kotlin

파이어베이스 이미지URL을 통해 이미지 불러오기

내가 구현하고싶었던 부분은 내가 게시글을 작성하고 업로드 버튼을 누르면, 게시글을 작성할때 첨부했던 이미지가 프레그먼트에 있는 리사이클러뷰에도 똑같이 적용되도록 구현하고 싶었다!!

 

 

# 문제상황

파이어베이스 imageUrl부분에 내가 파이어베이스 스토리지에 업로드 해놨던 이미지의 이미지url을 가져와야되는데,

어떻게 하는지 몰라서 그냥 key값을 넣어주었다...

일단 내가 잘못생각했던 부분은 업로드 된 이미지를 downloadUrl을 사용해서 url을 가져와서 적용해야되는데 그렇게 하지 않았다는것이다... 

class RecipeBookWriteActivity : AppCompatActivity() {

    private lateinit var binding : ActivityRecipebookWriteBinding

    private val TAG = RecipeBookWriteActivity::class.java.simpleName

    val storage = Firebase.storage


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

        binding = DataBindingUtil.setContentView(this, R.layout.activity_recipebook_write)


        // 업로드 버튼 클릭했을때
        binding.recipeUploadbutton.setOnClickListener {


            // 내가 입력한 recipenameET, foodmeterialET, recipewriteET, recipeTipET 값 받아옴
            val recipename = binding.recipenameET.text.toString()
            val foodmeterial = binding.foodmeterialET.text.toString()
            val recipewrite = binding.recipewriteET.text.toString()
            val recipetip = binding.recipeTipET.text.toString()
            // uid값 가져옴
            val uid = FBAuth.getUid()
            // time값 가져옴
            val time = FBAuth.getTime()



            Log.d(TAG, recipename)
            Log.d(TAG, foodmeterial)
            Log.d(TAG, recipewrite)
            Log.d(TAG, recipetip)



            // 키값 받아오기 (키값 알아내기)
            val key = FBRef.recipeboardRef.push().key.toString()



            // 파이어베이스에 데이터 집어넣기
            // recipeboard - key(임의의값) - RecipeBoardModel(recipename, foodmeterial, recipewrite, uid, time)
            FBRef.recipeboardRef
                .child(key)
                .setValue(RecipeBoardModel(recipename, foodmeterial, recipewrite, recipetip, uid, time, "${key}.png"))

            Toast.makeText(this, "레시피 업로드완료", Toast.LENGTH_SHORT).show()


            // 파이어베이스 스토리지에 이미지 업로드!!! (키값을 기준으로)
            imageUpload(key)



            // 엑티비티 사라짐
            finish()

        }


        // recipeimageUpload 클릭시
        binding.recipeimageUpload.setOnClickListener {


            // 갤러리로 이동
            val gallery = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.INTERNAL_CONTENT_URI)
            startActivityForResult(gallery, 100)


        }
    }


    // 이미지 업로드 함수
    private fun imageUpload(key :String){

        val storageRef = storage.reference
        val mountainsRef = storageRef.child(key + ".png")

        // 이미지 보일 레이아웃 위치 지정
        val imageView = binding.recipeimageUpload

        imageView.isDrawingCacheEnabled = true
        imageView.buildDrawingCache()
        val bitmap = (imageView.drawable as BitmapDrawable).bitmap
        val baos = ByteArrayOutputStream()
        bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos)
        val data = baos.toByteArray()

        var uploadTask = mountainsRef.putBytes(data)

        uploadTask.addOnFailureListener {


        }.addOnSuccessListener { taskSnapshot ->


        }

    }




    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)

        // 갤러리에서 데이터 받아오기
        if(resultCode == RESULT_OK && requestCode == 100){

            // 받아온 데이터를 레이아웃에 표시(갤러리 데이터가 recipeimageUpload에 표시)
            binding.recipeimageUpload.setImageURI(data?.data)
        }
    }




}

<원래코드> - 수정 전 코드

 

 

 

그러고나서 파이어베이스에 가서 확인해보니깐 당연하게도 이미지가 적용이 안되는것을 확인할수있었다.... 그냥 키값을 넣었기때문에 키값이 들어가있었다 

어떻게 구현할지 몰라서 무작정 키값을 집어넣었었다,,,ㅎㅎ

 

 

 

 

# 해결방안

class RecipeBookWriteActivity : AppCompatActivity() {

    private lateinit var binding : ActivityRecipebookWriteBinding

    private val TAG = RecipeBookWriteActivity::class.java.simpleName

    val storage = Firebase.storage

    // 이미지 업로드 노
    private var isImageUploade = false






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

        binding = DataBindingUtil.setContentView(this, R.layout.activity_recipebook_write)


        // 업로드 버튼 클릭했을때
        binding.recipeUploadbutton.setOnClickListener {


            // 키값 받아오기 (키값 알아내기)
            val key = FBRef.recipeboardRef.push().key.toString()



            // 이미지 업로드가 되어있다면
            if (isImageUploade == true) {

                // 파이어베이스 스토리지에 이미지 업로드!!! (키값을 기준으로)
                imageUpload(key)

            }


            // 엑티비티 사라짐
            finish()

        }






        // recipeimageUpload 클릭시
        binding.recipeimageUpload.setOnClickListener {


            // 갤러리로 이동
            val gallery = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.INTERNAL_CONTENT_URI)
            startActivityForResult(gallery, 100)

            isImageUploade = true


        }
    }





    // 이미지 업로드 함수
    private fun imageUpload(key :String){

        val storageRef = storage.reference
        val mountainsRef = storageRef.child(key + ".png")

        // 이미지 보일 레이아웃 위치 지정
        val imageView = binding.recipeimageUpload

        imageView.isDrawingCacheEnabled = true
        imageView.buildDrawingCache()
        val bitmap = (imageView.drawable as BitmapDrawable).bitmap
        val baos = ByteArrayOutputStream()
        bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos)
        val data = baos.toByteArray()


        var uploadTask = mountainsRef.putBytes(data)
        // 이미지 업로드 실패했을때
        uploadTask.addOnFailureListener {

            // 이미지 업로드 성공했을때
        }.addOnSuccessListener { taskSnapshot ->

            // 업로드 된 이미지의 다운로드 url을 가져온다
            mountainsRef.downloadUrl.addOnSuccessListener { uri ->

                val imageUrl = uri.toString()

                // saveDataToDatabase함수에, 생성된 key와 imageUrl넣기
                saveDataToDatabase(key, imageUrl)


                // 업로드 된 이미지의 다운로드 url을 가져오는것애 실패했을때
            }.addOnFailureListener {

            }

        }

    }




    // 파이어베이스 리얼타임 데이터베이스에 데이터 집어넣는 함수 (key와 imageUrl 사용해서!!)
    private fun saveDataToDatabase(key : String, imageUrl : String){


        // 내가 입력한 recipenameET, foodmeterialET, recipewriteET, recipeTipET 값 받아옴
        val recipename = binding.recipenameET.text.toString()
        val foodmeterial = binding.foodmeterialET.text.toString()
        val recipewrite = binding.recipewriteET.text.toString()
        val recipetip = binding.recipeTipET.text.toString()
        // uid값 가져옴
        val uid = FBAuth.getUid()
        // time값 가져옴
        val time = FBAuth.getTime()



        Log.d(TAG, recipename)
        Log.d(TAG, foodmeterial)
        Log.d(TAG, recipewrite)
        Log.d(TAG, recipetip)


        // 데이터모델에 imageUrl 저장
        val recipeBoardModel = RecipeBoardModel(recipename, foodmeterial, recipewrite, recipetip, uid, time, imageUrl)

        // 데이터 집어넣기 (파이어베이스 리얼타임 데이터베이스에)
        FBRef.recipeboardRef
            .child(key)
            .setValue(recipeBoardModel)

            // 데이터 집어넣기 성공한 경우
            .addOnSuccessListener {
                Toast.makeText(this, "레시피 업로드 완료", Toast.LENGTH_SHORT).show()

                finish()

            // 데이터 집어넣기 실패한 경우
            }.addOnFailureListener {
                Toast.makeText(this, "레시피 업로드에 실패했습니다", Toast.LENGTH_SHORT).show()
            }


    }












    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)

        // 갤러리에서 데이터 받아오기
        if(resultCode == RESULT_OK && requestCode == 100){

            // 받아온 데이터를 레이아웃에 표시(갤러리 데이터가 recipeimageUpload에 표시)
            binding.recipeimageUpload.setImageURI(data?.data)
        }
    }




}

 

먼저 saveDataToDatabase함수를 만들어준다

key값과 imageUrl이 필요하므로 데이터타입을 String으로 지정해서 만들어준다

그리고 이 함수에서 내가 입력한 값들을 받아온뒤, 데이터모델에 저장해주는데 imageUrl부분에는 아까 지정한 imageUrl값을 넣어준다

그리고 getReference를 사용해서 파이어베이스에 해당 데이터를 집어넣어준다!!

 

 

이제 원래 있던던 imageUpload함수에 아래 내용을 추가해줘야한다!!!

 

이미지 업로드 성공했을때, downloadUrl을 사용해서 업로드 된 이미지의 다운로드 url을 가져오고

saveDataToDatabase함수에 생성된 key값과 imageUrl값을 넣어준다

 

이렇게하고 파이어베이스를 실행시켜보면

스토리지에 내가 업로드한 이미지가 잘 업로드 되어있고

 

 

리얼타임 데이터베이스에 imageUrl에 내가 올렸던 이미지 url값이 잘 들어간것을 확인해볼수있다!!

 

 

실제 앱을 확인했을때 리사이클러뷰에도 올린 이미지가 잘 표시되는것을 확인할수있다!!

 

 

 

 

 

 

# 파이어베이스 이미지URL을 통해 이미지 불러오는 방법 전체코드

이제는 전체코드를 작성해보겠다 

 

먼저 나는 프레그먼트 안에 있는 리사이클러뷰에 이미지를 표시하고싶었기때문에

그 리스트 아이템에 이미지를 추가해줬다 (나는 간단하게 회색 네모 이미지를 추가해줬다)

이때 이미지의 width와 height를 지정해줘야한다! 그래야지 업로드했을때 이미지 크기가 일정하게 업로드된다

recipebook_list_item.xml

 

 

그리고 RecipeBoardModel의 데이터모델에 imageUrl을 추가해줬다 (imageUrl이 들어갈 공간을 만들어줌)

class RecipeBoardModel(

    val recipename: String = "",   // 레시피 이름
    val foodmeterial: String = "", // 레시피 재료
    val recipe: String = "",       // 레시피(요리 레시피)
    val recipetip: String = "",    // 요리팁
    val uid: String = "",          // uid
    val time: String = "",         // 현재시간

    val imageUrl: String = ""       // 이미지 url

)

 

RecipeBoardModel.kt

 

 

그리고 연결되는 어뎁터의 ViewHolder부분에 아래와 같이 Glide를 통해서 이미지가 보이게 적용해줬다

    // 내가 작성한 데이터 넣어주기(연결)
    inner class ViewHolder(itemView1 : View) : RecyclerView.ViewHolder(itemView1){

        private lateinit var binding: RecipebookListItemBinding

        fun bindItems1(item : RecipeBoardModel) : View {
              
              
              ...


            // 내가 넣어준 이미지가 foodImage1에 적용되게 imageView에 선언
            val iamgeView = itemView.findViewById<ImageView>(R.id.foodImage1)

            // 이미지 보이게 적용
            Glide.with(context)
                .load(item.imageUrl)
                .into(iamgeView)



            return itemView

        }

    }

RecipeRVAdapter.kt

 

 

 

(이부분은 위에서 설명한 부분!)

그러고 나서 아까 설명한대로 saveDataToDatabase함수를 key값과 imageUrl을 통해 만들어주고, 데이터모델의 imageUrl부분에 받아온 imageUrl을 넣어주고 파이어베이스에 데이터를 집어넣어준다

 

그리고 imageUpload함수에 이미지 업로드 성공했을때 downloadUrl을 사용해서 업로드 된 이미지의 다운로드url을 가져오고, saveDataToDatabase함수에 생성된 key값과 imageUrl값을 넣어준다

class RecipeBookWriteActivity : AppCompatActivity() {

    private lateinit var binding : ActivityRecipebookWriteBinding

    private val TAG = RecipeBookWriteActivity::class.java.simpleName

    val storage = Firebase.storage

    // 이미지 업로드 노
    private var isImageUploade = false






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

        binding = DataBindingUtil.setContentView(this, R.layout.activity_recipebook_write)


        // 업로드 버튼 클릭했을때
        binding.recipeUploadbutton.setOnClickListener {


            // 키값 받아오기 (키값 알아내기)
            val key = FBRef.recipeboardRef.push().key.toString()



            // 이미지 업로드가 되어있다면
            if (isImageUploade == true) {

                // 파이어베이스 스토리지에 이미지 업로드!!! (키값을 기준으로)
                imageUpload(key)

            }


            // 엑티비티 사라짐
            finish()

        }






        // recipeimageUpload 클릭시
        binding.recipeimageUpload.setOnClickListener {


            // 갤러리로 이동
            val gallery = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.INTERNAL_CONTENT_URI)
            startActivityForResult(gallery, 100)

            isImageUploade = true


        }
    }





    // 이미지 업로드 함수
    private fun imageUpload(key :String){

        val storageRef = storage.reference
        val mountainsRef = storageRef.child(key + ".png")

        // 이미지 보일 레이아웃 위치 지정
        val imageView = binding.recipeimageUpload

        imageView.isDrawingCacheEnabled = true
        imageView.buildDrawingCache()
        val bitmap = (imageView.drawable as BitmapDrawable).bitmap
        val baos = ByteArrayOutputStream()
        bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos)
        val data = baos.toByteArray()


        var uploadTask = mountainsRef.putBytes(data)
        // 이미지 업로드 실패했을때
        uploadTask.addOnFailureListener {

            // 이미지 업로드 성공했을때
        }.addOnSuccessListener { taskSnapshot ->

            // 업로드 된 이미지의 다운로드 url을 가져온다
            mountainsRef.downloadUrl.addOnSuccessListener { uri ->

                val imageUrl = uri.toString()

                // saveDataToDatabase함수에, 생성된 key와 imageUrl넣기
                saveDataToDatabase(key, imageUrl)


                // 업로드 된 이미지의 다운로드 url을 가져오는것애 실패했을때
            }.addOnFailureListener {

            }

        }

    }




    // 파이어베이스 리얼타임 데이터베이스에 데이터 집어넣는 함수 (key와 imageUrl 사용해서!!)
    private fun saveDataToDatabase(key : String, imageUrl : String){


        // 내가 입력한 recipenameET, foodmeterialET, recipewriteET, recipeTipET 값 받아옴
        val recipename = binding.recipenameET.text.toString()
        val foodmeterial = binding.foodmeterialET.text.toString()
        val recipewrite = binding.recipewriteET.text.toString()
        val recipetip = binding.recipeTipET.text.toString()
        // uid값 가져옴
        val uid = FBAuth.getUid()
        // time값 가져옴
        val time = FBAuth.getTime()



        Log.d(TAG, recipename)
        Log.d(TAG, foodmeterial)
        Log.d(TAG, recipewrite)
        Log.d(TAG, recipetip)


        // 데이터모델에 imageUrl 저장
        val recipeBoardModel = RecipeBoardModel(recipename, foodmeterial, recipewrite, recipetip, uid, time, imageUrl)

        // 데이터 집어넣기 (파이어베이스 리얼타임 데이터베이스에)
        FBRef.recipeboardRef
            .child(key)
            .setValue(recipeBoardModel)

            // 데이터 집어넣기 성공한 경우
            .addOnSuccessListener {
                Toast.makeText(this, "레시피 업로드 완료", Toast.LENGTH_SHORT).show()

                finish()

            // 데이터 집어넣기 실패한 경우
            }.addOnFailureListener {
                Toast.makeText(this, "레시피 업로드에 실패했습니다", Toast.LENGTH_SHORT).show()
            }


    }












    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)

        // 갤러리에서 데이터 받아오기
        if(resultCode == RESULT_OK && requestCode == 100){

            // 받아온 데이터를 레이아웃에 표시(갤러리 데이터가 recipeimageUpload에 표시)
            binding.recipeimageUpload.setImageURI(data?.data)
        }
    }




}

RecipeBookWriteActivity.kt

 

끝!! 이렇게하면 이미지가 잘 업로드 된다

 

 

 

 

Android에서 Cloud Storage로 파일 다운로드  |  Cloud Storage for Firebase (google.com)

 

Android에서 Cloud Storage로 파일 다운로드  |  Cloud Storage for Firebase

의견 보내기 Android에서 Cloud Storage로 파일 다운로드 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. Firebase용 Cloud Storage를 사용하면 Firebase에서 제공하고 관리

firebase.google.com

 

공식문서에서 "URL을 통해 데이터 다운로드" 부분을 참고하자!!