Topic = FIrebase Storage 통해 이미지 넣고 불러오기 + Metadata 간단한 개념
Why Firebase DB에 이미지를 저장하기 위해서 Storage를 사용하는 이유
- Firebase Realtime DB, Cloud Firestore에도 uri 값을 저장하는 방식과 Base64로 변환하는 등의 방식으로 이미지를 저장하고 불러올 수 있지만, DB에 이미지 파일을 포함할 경우 DB 용량 크기의 문제 등으로 DB 상호작용의 성능 저하를 일으키는 원인이 된다.
Storage를 사용할 경우 얻는 이점은 다음과 같다.
- 용량이 큰 이미지 파일을 DB와 분리하여 관리함으로 DB의 성능 저하를 방지할 수 있다.
- FIrebase Storage는 CDN ( Content Delivery Network )을 통해 이미지 파일을 제공하는데 이는 Firebase DB에서 이미지를 불러오는 것보다 빠른 속도를 제공한다고 한다...
그래도 되게 늦게 온다 - Firebase Console에서도 원하는 이미지로 수정이 가능하는 등 유연한 업데이트가 가능하다
Firebase Storage
- FirebaseStorage는 클라우드 저장소 서비스로 이미지, 동영상, 문서 등을 저장하고 관리할 수 있도록 한다.
- Storage도 Firebase에서 제공하는 서비스이기 때문에 Firebase Console에서 사용등록을 해주어야 한다.
주요 개념
- Bucket
- Firebase Storage에 저장되는 파일은 *버킷에 저장된다.
- *버킷
- 버킷이란 파일을 정장하는 논리적인 단위를 한다, 이는 실제로 존재하는 하드웨어 컨테이너가 존재하는 것이 아니라, 파일을 그룹화하고 구조화하기 위한 개념이다.
- Firebase Storage에 저장되는 파일은 *버킷에 저장된다.
- Path
- 경로에 저장하는 방식으로는 일반적으로 버킷이름 / 파일 경로 / ( gs://project-bucket/images/user1.jpg ) 의 형식으로 위치를 통한 접근이 가능하다.
- 보안및 편의성
- Realtime DB, Firestore와 마찬가지로 특정 그룹, 사용자 만이 이미지에 액세스 할 수 있도록 설정할 수 있다.
- Firebase Storage에 업로드된 파일에 대한 고유한 URL을 제공한다.
Storage [ 1 ] Firebase Console 설정
- Storage 또한 Firebase의 서비스 중 하나이기 때문에 Console에서 사용설정을 해주어야 한다.
- 앱에서 사용자가 Firebase Storage에 파일을 저장 및 읽을 수 있게 보안규칙의 read, write 를 true로 설정해준다.
Storage [ 2 ] Android Studio Project 설정 Manifest, build.gradle
Manifest - INTERNET permission 허용
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.INTERNET"/>
<application
...
build.gradle - Glide ( 이미지 로더 Library 중 선호하는 Library 사용 )와 Storage,auth 추가.
dependencies {
implementation(platform("com.google.firebase:firebase-bom:32.3.1"))
...
implementation("com.google.firebase:firebase-auth-ktx")
implementation("com.google.firebase:firebase-storage-ktx")
implementation ("com.github.bumptech.glide:glide:4.14.2")
Storage [ 3 ] 단일 이미지 데이터 저장
class mypage : Fragment() {
// 이미지를 불러올 때에도 사용할 것이므로 전역변수로 사용
private lateinit var photoUri: Uri
private var _binding : FragmentBinding? = null
private val binding get() = _binding!!
// UID에 해당 하는 이미지로 Mypage의 이미지를 변경할 것이므로 예제에서 필요하다.
private lateinit var auth : FirebaseAuth
...
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.btnProfileImgEdit.setOnClickListener {
pickMedia.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly))
}
binding.completeButton.setOnClickListener {
val address = binding.profileAddress.text.toString()
val nickname = binding.profileNickname.text.toString()
val intro = binding.profileText.text.toString()
imageUpload()
profileUpload(nickname,intro,address)
}
}
private fun imageUpload() {
val uploadTask = storage.reference.child("images").child("${auth.currentUser!!.uid}")
.putFile(photoUri!!)
uploadTask.addOnSuccessListener {
// 파일 저장 성공 시 이벤트
Log.d("xxxx", " img upload successful ")
}.addOnFailureListener {
// 파일 저장 실패 시 이벤트
Log.d("xxxx", " img upload failure : $it ")
}
}
// Intent.ACTION_PICK 등 편한 방법으로 이미지 가져오는 함수 지정해주기.
private val pickMedia =
registerForActivityResult(ActivityResultContracts.PickVisualMedia()) { uri ->
if (uri != null) {
Log.d("xxxx", "selected URI : $uri")
photoUri = uri
Glide.with(this)
.load(photoUri)
.into(binding.profileImage)
} else {
Log.d("xxxx", "No media selected ")
}
}
}
- Firebase DB에 데이터를 넣는 방식과 유사한 방식으로 Storage에 파일을 저장해준다.
- addOnSuccessListener, FailureListener를 통해 처리여부를 확인할 수 있으며 Console 에서도 정상적으로 저장되었는지 확인해주면 된다
파일이 정상적으로 저장된 것을 확인할 수 있다.
Storage [ 4 ] 데이터 불러오기
먼저 [ 3 ] 에서 작성했던 코드와 같은 클래스 내부에서 ... 에 해당하는 onCreateView() 코드이다.
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = FragmentMypageProfileBinding.inflate(inflater, container, false)
val downloadTask = storage.reference.child("images").child("${auth.currentUser!!.uid}")
.downloadUrl
downloadTask.addOnSuccessListener{
photoUri = it
Log.d("xxxx", "downloadTask Successful uri : $it")
if (it != null)
Glide.with(this)
.load(photoUri)
.into(binding.profileImage)
}.addOnFailureListener{
Log.d("xxxx", "downloadTask Failure Exception : $it")
}
return binding.root
}
- 저장된 파일의 경로에 접근해 해당 파일을 downloadUrl 메서드를 통해 불러온 뒤 photoUri 값으로 지정해주고, ImageLoader Glide를 통해 ImageView에서 확인할 수 있도록해준다.
Storage [ 5 ] 복수 이미지 데이터 저장
private val pickMultipleMedia =
registerForActivityResult(ActivityResultContracts.PickMultipleVisualMedia(10)) { uriList ->
if (uriList.isNotEmpty()) {
uriList.forEach {uri ->
Log.d("xxxx", "Selected URI: $uri")
}
uris = uriList.toMutableList()
Log.d("xxxx", "Edit Frag Number of items selected : ${uriList.size} ")
writePostImgAdapter.submitList(uriList)
writePostImgAdapter.notifyDataSetChanged()
} else {
Log.d("xxxx", "Edit Frag No media selected: ")
}
}
private fun imageUpload(){
val time = getTime()
uris?.let { uris ->
for (i in uris.indices) {
val uri = uris[i]
val fileName = "${time}_$i"
storage.reference.child("post").child(fileName).putFile(uri).addOnSuccessListener {
Log.d("xxxx", "Edit Frag imageUpload Successful : ${it}")
}.addOnFailureListener{
Log.d("xxxx", " Edit Frag imageUpload Failure : $it ")
}
}
}
}
➕ 10.23 추가
- registerForActivityResult(ActivityResultContracts.PickMultipleVisualMedia()) 를 통해 이미지를 가져오는 경우 이미지를 선택한 순서대로 리스트에 담기지 않는다. Intent.ACTION_PICK 으로 해도 마찬가지로 클릭된 순서대로 List에 들어가지 않기 때문에 추후에 추가 예정
Storage Metadata
Metadata 개념
- 이전에 Annotation 학습하면서 Metadata의 개념에 대해 간단하게 알아보았었는데 일부 내용 옮기면서 복습해보자.
- 메타데이터 ?
- 메타 데이터는 특정 데이터에 대한 추가적인 세부 사항을 제공하여 해당 데이터를 효율적으로 인식할 수 있도록 해주는 것으로, 어떤 데이터의 구조화 된 정보를 분석, 분류하고 부가적 정보를 추가하기 위해 그 데이터 뒤에 함께 따라가는 정보를 한다. 사진을 촬영하는 경우 해당 사진 데이터의 촬영 시간, 어떤 카메라로 찍었는지, 촬영 위치를 별도로 저장되는 경우가 있는데, 이러한 데이터들을 메타 데이터라고 할 수 있다.
- 자세한 개념은 위키백과 참조
Storage Metadata 적용하기
Storage에서는 Metadata를 통해 파일에 대한 추가정보를 파일과 함께 저장할 수 있으며 Metadata를 활용하여 파일을 관리하고 필요한 정보를 추출할 수 있다.
저장하기
val metadata = StorageMetadata.Builder()
.setCustomMetadata("key1", "value1")
.setCustomMetadata("key2", "value2")
.build()
val uploadTask = storage.reference.child("images").child("${auth.currentUser!!.uid}")
.putFile(photoUri!!, metadata)
- 저장하는 방법은, putfile 시 해당 file과 metadata를 함께 저장해주면 된다.
불러오기
val uploadTask = storage.reference.child("images").child("${auth.currentUser!!.uid}")
.metadata.addOnsuccessListener { metadata ->
...
}
[ A. 오늘 복습한 내용 / B. 다음에 학습할 내용 ]
A. Metadata 개념 이해도 ( 87 / 100 )
B. 여러장의 사진을 선택하고 저장하는 기능 추후에 학습 후 추가 예정 ✔️10.21 추가
B. metadata를 통한 효율적인 storage 관리
[오류,에러 등등]
1. 특별한 에러는 없었다.
[느낀 점]
1. 코드 순서 , 생명주기 같은 개념이 어렵다.
2. 내가 쉬우면 남들도 쉽고 내가 어려우면 남들은 쉽다;;;
3. 몸 컨디션 관리를 해야할 것 같다.
[Reference]
// 이미지 권한 관련 - 다음에 권한관리에서 정리할 예정
// storage
https://rnfirebase.io/storage/usage
// metadata
https://ko.wikipedia.org/wiki/%EB%A9%94%ED%83%80%EB%8D%B0%EC%9D%B4%ED%84%B0
'TIL' 카테고리의 다른 글
[TIL] Kotlin Firestore, Storage [ 2 ] 이미지와 함께 게시글 데이터 가져오기 (2) | 2023.10.22 |
---|---|
[TIL] Kotlin Firebase Firestore, Storage [ 1 ] 이미지가 포함된 게시글 쓰기 (0) | 2023.10.21 |
[TIL] Kotlin Firebase - Realtime Database [ 2 ] 저장된 데이터Listener로 가져오기 (5) | 2023.10.19 |
[TIL] Kotlin Firebase Authentication [ 2 ] - 회원가입 시 추가로 회원정보 DB에 넣어주기. (0) | 2023.10.19 |
[TIL] Kotlin Firebase Dynamic Link ( 2025.08 Deprecated ) (2) | 2023.10.18 |