본문 바로가기

개발 노트/Kotlin

파이어베이스 이미지URL을 통해 이미지 수정하기

저번에 "파이어베이스 이미지URL을 통해 이미지 불러오기" 글을 쓴적이 있다

https://coding-juuwon2.tistory.com/212

 

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

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

coding-juuwon2.tistory.com

 

그때 쓴 게시글에 있는 내용과 비슷한 내용이다. (아니 거의 똑같은 내용이라고봐도 된다)

저번 게시글을 쓸때 다 이해했다고 생각했는데, 수정하는 코드를 짜려니깐 조금 버벅여서 기록해두려한다!

 

 

게시글을 수정할 수 있도록 구현하는중에, 수정버튼을 눌렀을때 텍스트는 다 리사이클러뷰에 반영되는데 이미지는 아직 반영되도록 해놓지 않아서

이미지를 수정하고 수정버튼을 눌렀을때, 리사이클러뷰에 수정된 이미지가 보이도록 해볼것이다

 

 

[수정전 코드] - 수정된 텍스트만 적용, 수정된 이미지는 적용되지 않음

 

수정 전 코드인데 수정된 텍스트만 적용되고, 수정된 이미지는 리사이클러뷰에 반영되지 않는 코드였다 

class RecipeEditActivity : AppCompatActivity() {

    // key값 선언
    private lateinit var key: String

    private lateinit var binding: ActivityRecipeEditBinding

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

    // uid 가져오기위해 초기화
    private lateinit var writeUid: String

    val storage = Firebase.storage


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

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


        // BoardInsideActivity에서 보낸 key값 받아오기
        key = intent.getStringExtra("key1").toString()


        // 게시글에서 쓴 데이터(글들) 가져와서 레시피수정 레이아웃에 표현하기
        getEditBoardData(key)

        // 게시글에서 쓴 이미지 가져와서 레이아웃에 표현하기
        getEditImageData(key)


        // editbutton버튼 눌렀을때
        binding.recipeUploadbutton1.setOnClickListener {
        
            // 수정되는 함수 실행
            editBoardData(key)

        }



        // imageArea 이미지 눌렀을때
        binding.recipeimageUpload1.setOnClickListener {

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

        }



    }


    // board데이터 받아오는 함수 (게시글에서 쓴 데이터 가져와서 수정 레아이웃에 표현하기)
    private fun getEditBoardData(key: String) {

        // 데이터 가져오기
        val postListener = object : ValueEventListener {
            override fun onDataChange(dataSnapshot: DataSnapshot) {


                // try문에서 에러발생하면 catch문 실행
                try {

                    //데이터 받아오기
                    val dataModel = dataSnapshot.getValue(RecipeBoardModel::class.java)

                    // 레이아읏과 연결
                    binding.recipenameET1.setText(dataModel!!.recipename)         // recipenameET1 와 recipename 연결
                    binding.foodmeterialET1.setText(dataModel.foodmeterial)     // foodmeterialET1 와 foodmeterial 연결
                    binding.recipewriteET1.setText(dataModel.recipe)            // recipewriteET1 와 recipe 연결
                    binding.recipeTipET1.setText(dataModel.recipetip)           // recipeTipET1 와 recipetip 연결


                    // uid가져옴(글쓴사람의 uid)
                    writeUid = dataModel.uid


                } catch (e: Exception) {
                    Log.d(TAG, "수정완료")
                }

            }

            override fun onCancelled(databaseError: DatabaseError) {
                // Getting Post failed, log a message
                Log.w(TAG, "loadPost:onCancelled", databaseError.toException())
            }
        }

        // board안에있는 key값을 가져오기
        FBRef.recipeboardRef.child(key).addValueEventListener(postListener)


    }


    // 이미지 다운로드 함수 (게시글에서 쓴 이미지 가져와서 레이아웃에 표현하기)
    private fun getEditImageData(key: String) {

        // 이미지 다운로드

        // Reference to an image file in Cloud Storage
        val storageReference = Firebase.storage.reference.child(key + ".png")

        // ImageView in your Activity
        val imageViewFromFB = binding.recipeimageUpload1

        storageReference.downloadUrl.addOnCompleteListener(OnCompleteListener { task ->

            // 이미지 업로드 성공
            if (task.isSuccessful) {
                // Glide를 사용하여 task에서 이미지 직접 다운로드
                Glide.with(this)
                    .load(task.result)
                    .into(imageViewFromFB)

                // 이미지 업로드 실패
            } else {

            }
        })

    }




    // 수정버튼 눌렀을때 수정(다시 업로드)되는 함수
    private fun editBoardData(key : String){

        // 데이터 집어넣기
        FBRef.recipeboardRef
            .child(key)
            .setValue(RecipeBoardModel(binding.recipenameET1.text.toString(),        // 내가 작성한 recipenameET1값 집어넣음
                                          binding.foodmeterialET1.text.toString(),   // 내가 작성한 foodmeterialET1값 집어넣음
                                          binding.recipewriteET1.text.toString(),    // 내가 작성한 recipewriteET1값 집어넣음
                                          binding.recipeTipET1.text.toString(),      // 내가 작성한 recipeTipET1값 집어넣음
                                          writeUid,
                                          FBAuth.getTime2() )
            )


        // 이미지 업로드
        imageUpload(key)


        Toast.makeText(this, "수정완료", Toast.LENGTH_SHORT).show()


        finish()    // 엑티비티 종료

    }



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

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

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


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

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

        val imageView = binding.recipeimageUpload1

        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 ->
           
        }
    }



}

 

 

 

[수정 후 코드]

 

일단 파이어베이스 리얼타임데이터베이스에 수정된 imageUrl이 들어가야되는데, 수정전코드에서는 imageUrl에 아무값도 들어가지 않았다

그래서 수정버튼을 눌렀을때 다시 파이어베이스에 업로드되는 함수인 editBoardData()함수

imageUrl = String 이라고 파라미터를 추가해준뒤, 파이어베이스에 imageUrl 데이터를 집어넣어준다 

수정 전 코드에서는 이 함수에서 이미지를 업로드하도록 imageUpload()함수를 추가해줬는데 여기서는 삭제해준다

 

그리고 이미지를 업로드하는 함수인 imageUpload()함수

이미지 업로드에 성공했다면, downloadUrl을 사용해서 파이어베이스에 업로드된 이미지의 url을 가져와서

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

 

마지막으로 수정버튼을 눌렀을때, imageUpload함수에 key값을 넣어서 실행되도록해준다

(원래는 수정버튼을 눌렀을때 editBoardData함수가 실행되도록 했었는데 그것은 삭제해준다

imageUpload함수 안에 이미 editBoardData함수를 넣어줬기 때문에 , imageUpload함수를 실행하면 editBoardData함수도 저절로 실행되기 때문이다)

- 나는 이부분에서 어떤 함수를 실행해줘야될지 몰라서 헤맸었다...

 

수정한 전체 코드이다 

class RecipeEditActivity : AppCompatActivity() {

    // key값 선언
    private lateinit var key: String

    private lateinit var binding: ActivityRecipeEditBinding

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

    // uid 가져오기위해 초기화
    private lateinit var writeUid: String

    val storage = Firebase.storage


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

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


        // BoardInsideActivity에서 보낸 key값 받아오기
        key = intent.getStringExtra("key1").toString()


        // 게시글에서 쓴 데이터(글들) 가져와서 레시피수정 레이아웃에 표현하기
        getEditBoardData(key)

        // 게시글에서 쓴 이미지 가져와서 레이아웃수정 레이아웃에 표현하기
        getEditImageData(key)



        // editbutton버튼 눌렀을때
        binding.recipeUploadbutton1.setOnClickListener {

//            // 수정되는 함수 실행 (삭제해주기) 
//            editBoardData(key)

        
        imageUpload(key)

        }



        // imageArea 이미지 눌렀을때
        binding.recipeimageUpload1.setOnClickListener {

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

        }



    }


    // board데이터 받아오는 함수 (게시글에서 쓴 데이터 가져와서 수정 레아이웃에 표현하기)
    private fun getEditBoardData(key: String) {

        // 데이터 가져오기
        val postListener = object : ValueEventListener {
            override fun onDataChange(dataSnapshot: DataSnapshot) {


                // try문에서 에러발생하면 catch문 실행
                try {

                    //데이터 받아오기
                    val dataModel = dataSnapshot.getValue(RecipeBoardModel::class.java)

                    // 레이아읏과 연결
                    binding.recipenameET1.setText(dataModel!!.recipename)         // recipenameET1 와 recipename 연결
                    binding.foodmeterialET1.setText(dataModel.foodmeterial)     // foodmeterialET1 와 foodmeterial 연결
                    binding.recipewriteET1.setText(dataModel.recipe)            // recipewriteET1 와 recipe 연결
                    binding.recipeTipET1.setText(dataModel.recipetip)           // recipeTipET1 와 recipetip 연결


                    // uid가져옴(글쓴사람의 uid)
                    writeUid = dataModel.uid


                } catch (e: Exception) {
                    Log.d(TAG, "수정완료")
                }

            }

            override fun onCancelled(databaseError: DatabaseError) {
                // Getting Post failed, log a message
                Log.w(TAG, "loadPost:onCancelled", databaseError.toException())
            }
        }

        // board안에있는 key값을 가져오기
        FBRef.recipeboardRef.child(key).addValueEventListener(postListener)


    }


    // 이미지 다운로드 함수 (게시글에서 쓴 이미지 가져와서 레이아웃에 표현하기)
    private fun getEditImageData(key: String) {

        // 파이어베이스 스토리지에 key.png라고 이름 등록
        val storageReference = Firebase.storage.reference.child(key + ".png")

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

        storageReference.downloadUrl.addOnCompleteListener(OnCompleteListener { task ->

            // 이미지 업로드 성공
            if (task.isSuccessful) {
                // Glide를 사용하여 task에서 이미지 직접 다운로드
                Glide.with(this)
                    .load(task.result)
                    .into(imageViewFromFB)

                // 이미지 업로드 실패
            } else {

            }
        })

    }




    // 수정버튼 눌렀을때 수정(다시 업로드)되는 함수
    // 파이어베이스 리얼타임데이터베이스에 다시 업로드, key값과 imageUrl값을 넣어서!!
    private fun editBoardData(key : String, imageUrl : String){

        // 데이터 집어넣기
        FBRef.recipeboardRef
            .child(key)
            .setValue(RecipeBoardModel(binding.recipenameET1.text.toString(),        // 내가 작성한 recipenameET1값 집어넣음
                binding.foodmeterialET1.text.toString(),   // 내가 작성한 foodmeterialET1값 집어넣음
                binding.recipewriteET1.text.toString(),    // 내가 작성한 recipewriteET1값 집어넣음
                binding.recipeTipET1.text.toString(),      // 내가 작성한 recipeTipET1값 집어넣음
                writeUid,
                FBAuth.getTime2(),
                imageUrl)               // 수정된 imageUrl값 업로드
            )



        Toast.makeText(this, "수정완료", Toast.LENGTH_SHORT).show()


        finish()    // 엑티비티 종료

    }



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

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

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


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

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

        val imageView = binding.recipeimageUpload1

        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()

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


                // 업로드 된 이미지의 다운로드 url을 가져오는것애 실패했을때
            }.addOnFailureListener {
                Toast.makeText(this, "이미지 업로드에 실패했습니다", Toast.LENGTH_SHORT).show()
            }

        }
    }



}

 

 

이렇게하면 수정버튼을 눌렀을때, 수정한 글과 이미지가 리사이클러뷰에 잘 반영되는것을 확인할 수 있다

 

 

파이어베이스도 확인해보겠다! 

이건 수정하기 전 파이어베이스이고 

 

 

수정하면 이렇게 모든 데이터가 수정이 잘 되는것을 확인할 수 있다

특히 내가 원했던 imageUrl부분도 내가 업로드한 이미지의 url이 잘 수정된것을 볼 수 있다!!