본문 바로가기
FireBase

Firebase - FirebaseStorage

by 봄석 2019. 1. 10.

Firebase - FirebaseStorage

FirebaseStorage는 일종의 문서, 사진, 파일, 동영상을 저장하는 저장소라고 생각하면 됩니다.

과거 FirebaseStorage가 없었을 때에는 직접 리눅스 서버에 vsFTPd를 설치해서 파일서버를 만들어주거나 아니면 아마존 s3나 Azure의 block을 사서 써야 했습니다. 금액을 떠나서 과정이 복잡하고 까다로워서 따로 서버 개발자가 아니면 구축하기가 힘들었습니다 . 

하지만 FirebaseStorage라이브러리를 설치하여 이용하면 손쉽게 FTP 서버를 구축할 수있습니다.



Firebase 콘솔에 내앱 추가하기, 프로젝트에 라이브러리 추가하기

먼저 https://console.firebase.google.com/ Firebase 콘솔창에 접속해 

Project Overview오른쪽의 톱니바퀴를 눌러 

 프로젝트설정-> 앱추가 -> 패키지이름 입력 

->google-services.json 파일 다운로드하여 -> 안드로이드스튜디오 app 아래에 넣어주기



그리고 gradle.build(모듈)의 dependencies에 아래 내용을 추가합니다

dependencies {
      ...
 
    //Firebase
    implementation 'com.google.firebase:firebase-database:16.0.5'
    implementation 'com.google.firebase:firebase-auth:16.0.5'
    
}

apply plugin: "com.google.gms.google-services"


그리고 gradle.build(프로젝트)

buildscript {
    ext.kotlin_version = '1.3.11'
    ext.anko_version='0.10.5'
    repositories {
        google()
        jcenter()
    }
    dependencies {
        ...
 
        classpath 'com.google.gms:google-services:4.2.0' // update version
 
    }
}
 
allprojects {
    repositories {
        ...
        maven{
            url "https://maven.google.com"
        }
    }
}
 
task clean(type: Delete) {
    delete rootProject.buildDir

}



그리고 gradle.build의 dependencies에 아래 내용도 추가해줍니다

//FirebaseStorage

implementation 'com.google.firebase:firebase-storage:16.0.5'




그리고 파이어 베이스 콘솔로 이동합니다



콘솔에서 왼쪽의 storage 클릭 하고 시작하기


Firebase는 자동적으로 보안 규칙을 만들어줍니다. 위의 보안 규칙은 

인증된 사용자만 사용할 수 있도록 지정되며 가장 기본적인 보안 규칙입니다.


storage에서 규칙을 누르면 보안 규칙을 보고 또 수정도 할 수 있습니다.


보안 규칙의 종류


인증된 사용자

1
allow read, wirte: if request.auth != null;
cs

인증된 사용자는 Firebase의 Authentication에 등록된 사용자만 사용할 수 있는 보안규칙으로 

외부에서 제삼자가 악용하는 것을 막을 수 있습니다.


인증되지 않은 사용자

allow read, wirte: if true ;

Firebase Authentication에 등록되지 않은 사용자도 사용이 가능하며 개발을  위해서 빠른 테스트 요구할 때 많이 사용됩니다.


쓰기 금지

allow read, wirte: if false;

데이터베이스에 인증된 사용자든 아니든  쓰기를 금지합니다.


변수 선언, 인스턴스 얻기

class MainActivity : AppCompatActivity() {
    lateinit var firebaseStorage:FirebaseStorage
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        
        firebaseStorage= FirebaseStorage.getInstance()
        
    }
}




Storage 업로드 하기

업로드 방법에는 여러가지가 있습니다. 

1) ByteArrayOutputStream, FileInputStream(메모리)에 있는 파일을 올리는 방법

2) Uri(경로) 파일을 올리는방법

3) 직접 파일을 올리는 방법



1. ByteArrayOutputStream 업로드

private fun upLoadFromMemory(bitmap: Bitmap){
        var baos =ByteArrayOutputStream()
        bitmap.compress(Bitmap.CompressFormat.JPEG,100,baos)
        var data=baos.toByteArray()
 
        firebaseStorage?.reference.child("imageForder").child("imageBaos.png")
            .putBytes(data).addOnCompleteListener {
                if(it.isSuccessful){
                    toast("업로드 성공")
                }
            }
    }


메모리의 ByteArrayOutputStream에 있는 스트림 코드 putBytes를 통해 바로 FirebaseStorage로 올릴 수 있습니다.


2. FileInputStream 업로드

private fun uploadStream(file: File){
        val stream =FileInputStream(file)
 
        firebaseStorage?.reference.child("imageFolder2").child("imageStream.png")
            .putStream(stream)
            .addOnCompleteListener {
                if(it.isSuccessful){
                    toast("업로드 성공")
                }
            }
    }


메모리의 FileInputStream에 있는 스트림 코드를 putStream으로 바로 FirebaseStorage로 업로드


3. FileUri(파일 경로 이용)업로드

 private fun uploadUri(file:File){
        val file = Uri.fromFile(file)
        firebaseStorage?.reference.child("imageFolder3").child("imageUri.png")
            .putFile(file).addOnCompleteListener {
                if(it.isSuccessful){
                    toast("업로드성공")
                }
            }
    }


Uri.from을 통해서 저장된 파일의 경로로 접근해서 바로 putFile로 파일을 올릴수 있습니다.




Storage 에서 다운로드하기

다운로드 방법에는 대표적으로 두가지가 있습니다.


1) 메모리에 다운로드 받는 방법

2) 스토리지에 저장하는 방법


둘의 중요한 차이점은 메모리로 저장하는 것은 앱이 종료되었을 때 저장된 데이터가 날아가는 

방식입니다. 스토리지에 저장하는 방법은 앱이 종료되거나 스마트폰이 재부팅 되어도 데이터가 남아있습니다.


스토리지 방식은 우선 읽는 속도가 매우 느리며 자주 사용할 경우 쌓인 다운로드 데이터를 일일이 지워주어야 하는 단점이 있습니다.


다운로드 받기 위해서는 URL 주소가 필요합니다 . 발급받는 방법은 다운로드 완료 후 

addOnCompleteListener 결과값이 넘어올 때 task.result.downloadUrl로 URL 주소를 받아오면 됩니다.


1. 메모리 저장

//메모리저장 다운로드 방법
    private fun downloadInMemory() {
        var ref = firebaseStorage.reference.child("imageFolder2").child("imageStream.png")
        var ONE_MEGABYTE: Long = 1024 * 1024
        ref?.getBytes(ONE_MEGABYTE).addOnCompleteListener {
            if (it.isSuccessful)
                toast("이미지 다운로드 성공")
            imageView.setImageBitmap(byteArrayToBitmap(it.result!!))
        }
    }
 
    private fun byteArrayToBitmap(byteArry: ByteArray): Bitmap {
        var bitmap:Bitmap?=null
        bitmap =BitmapFactory.decodeByteArray(byteArry,0,byteArry.size)
        return bitmap
    }


\

메모리 다운로드는 다운로드된 파일을 메모리에 저장할때 사용합니다.

FirebaseStorage.getInstance().reference.child(storage저장폴더명).child(파일이름)

.getBytes(Byte).addOnCompleteListener에서 다운로드된 byteArray를 받을 수 있습니다.



2. 스토리지 저장

스토리지 저장은 직접 안드로이드 폰 저장소에 저장하는 기능입니다.

메모리에 저장하고 싶지 않고 직접 안드로이드 저장소에 저장하고 싶을때 사용합니다.

 //스토리지 다운로드 저장
    private fun downloadInLocal(){
        var ref=firebaseStorage?.reference.child("imageFolder2").child("imageStream.png")
        var localFile=File.createTempFile("localImages","png")
        ref.getFile(localFile).addOnSuccessListener {
            toast("이미지 다운로드 성공")
 
        }
    }


사실 직접적으로 경로로 접근해서 쓰는 경우는 거의 없습니다.

직접적인 경로를 사용한다는 것은 보안상 좋지 않으며 또한 남들에게 파일이나 이미지를 공유할 때 굉장히 복잡한 경로로 제공해야 되는 경우가 발생합니다. 그렇기 때문에 

보통 DownLoadUri 등을 통해서 URL 경로를 받아서 사용합니다.


FirebaseFileDownload 방법 모두 보기 - https://firebase.google.com/docs/storage/android/download-files?hl=ko#manage_downloads




파일 삭제하기

  //파일삭제
    private fun deleteFile(){
        var ref=firebaseStorage?.reference.child("imageFolder3/imageUri.png")
        ref.delete().addOnCompleteListener {
            if(it.isSuccessful){
                toast("삭제완료")
            }
        }
    }



파일을 삭제하기 위해서는 간단히 파일 경로를 입력한 뒤 delete() 코드를 입력하면 됩니다.





위 모든 예 코드(권한요청, 변환 등등포함됨)

MainActivity.kt

class MainActivity : AppCompatActivity() {
    private val REQUEST_READ_EXTERNAL_STORAGE = 1000
 
    lateinit var firebaseStorage: FirebaseStorage
    val file: File by lazy {
        File("/sdcard/Pictures/soon.jpg")
    }
 
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
 
        firebaseStorage = FirebaseStorage.getInstance()
 
        /*
        //비트맵을 바이트어레이로 변경하여 업로드
        var bitmap=getBitmapFromVectorDrawable(R.drawable.ic_android_black_24dp)
        upLoadFromMemory(bitmap!!)
 
 
        //퍼미션 체크하면서 sdcard에 있는 이미지 FileStream(메모리)의 파일 업로드
        //퍼미션 체크하면서 sdcard에 있는 Uri로 파일 업로드
        permissionCheck()
 
 
        //메모리에 다운로드
        downloadInMemory()
 
        //스토리지에 다운 완료
        downloadInLocal()
        */
 
        //파일 삭제
        deleteFile()
    }
 
    //ByteArrayOuputStream 사용
    private fun upLoadFromMemory(bitmap: Bitmap) {
        var baos = ByteArrayOutputStream()
        bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos)
        var data = baos.toByteArray()
 
        firebaseStorage?.reference.child("imageForder").child("imageBaos.png")
            .putBytes(data).addOnCompleteListener {
                if (it.isSuccessful) {
                    toast("업로드 성공")
                }
            }
    }
 
    //FileInputStream 사용
    private fun uploadStream(file: File) {
        val stream = FileInputStream(file)
 
        firebaseStorage?.reference.child("imageFolder2").child("imageStream.png")
            .putStream(stream)
            .addOnCompleteListener {
                if (it.isSuccessful) {
                    toast("업로드 성공")
                }
            }
    }
 
    //Fileuri 사용
    private fun uploadUri(file: File) {
        val file = Uri.fromFile(file)
        firebaseStorage?.reference.child("imageFolder3").child("imageUri.png")
            .putFile(file).addOnCompleteListener {
                if (it.isSuccessful) {
                    toast("업로드성공")
                }
            }
    }
 
    //메모리저장 다운로드 방법
    private fun downloadInMemory() {
        var ref = firebaseStorage.reference.child("imageFolder2").child("imageStream.png")
        var ONE_MEGABYTE: Long = 1024 * 1024
        ref?.getBytes(ONE_MEGABYTE).addOnCompleteListener {
            if (it.isSuccessful)
                toast("이미지 다운로드 성공")
            imageView.setImageBitmap(byteArrayToBitmap(it.result!!))
        }
    }
    //byteArray to Bitmap
    private fun byteArrayToBitmap(byteArry: ByteArray): Bitmap {
        var bitmap:Bitmap?=null
        bitmap =BitmapFactory.decodeByteArray(byteArry,0,byteArry.size)
        return bitmap
    }
    //스토리지 다운로드 저장
    private fun downloadInLocal(){
        var ref=firebaseStorage?.reference.child("imageFolder2").child("imageStream.png")
        var localFile=File.createTempFile("localImages","png")
        ref.getFile(localFile).addOnSuccessListener {
            toast("이미지 다운로드 성공")
 
        }
    }
    //파일삭제
    private fun deleteFile(){
        var ref=firebaseStorage?.reference.child("imageFolder3/imageUri.png")
        ref.delete().addOnCompleteListener {
            if(it.isSuccessful){
                toast("삭제완료")
            }
        }
    }
 
    //벡터 드로어블을 Bitmap으로 변환
    private fun getBitmapFromVectorDrawable(drawableId: Int): Bitmap? {
        var drawable = ContextCompat.getDrawable(this, drawableId) ?: return null
 
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
            drawable = DrawableCompat.wrap(drawable).mutate()
        }
 
        val bitmap = Bitmap.createBitmap(
            drawable.intrinsicWidth,
            drawable.intrinsicHeight,
            Bitmap.Config.ARGB_8888
        ) ?: return null
        val canvas = Canvas(bitmap)
        drawable.setBounds(0, 0, canvas.width, canvas.height)
        drawable.draw(canvas)
 
        return bitmap
    }
 
    private fun permissionCheck() {
        //권한이 부여되었는지 확인
        if (ContextCompat.checkSelfPermission(
                this,
                Manifest.permission.READ_EXTERNAL_STORAGE
            ) != PackageManager.PERMISSION_GRANTED
        ) {
 
            //권한이 허용되지 않음
            if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.READ_EXTERNAL_STORAGE)) {
                //이전에 이미 권한이 거부되었을 때 설명
                alert(
                    "사진 정보를 얻으려면 외부 저장소 권한이 필수로 필요합니다",
                    "권한이 필요한 이유"
                ) {
                    yesButton {
                        //권한 요청
                        ActivityCompat.requestPermissions(
                            this@MainActivity,
                            arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE),
                            REQUEST_READ_EXTERNAL_STORAGE
                        )
                    }
                    noButton { }
                }.show()
            } else {
                //권한요청
                ActivityCompat.requestPermissions(
                    this,
                    arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE),
                    REQUEST_READ_EXTERNAL_STORAGE
                )
            }
 
        } else {
            //권한이 이미 허용됨
            uploadStream(file)//stream
            uploadUri(file)//uri
        }
    }
 
    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        when (requestCode) {
            REQUEST_READ_EXTERNAL_STORAGE -> {
                if ((grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED)) {
                    //권한 허용됨
                    uploadStream(file)  //stream
                    uploadUri(file) //uri
                } else {
                    //권한 거부
                    toast("권한 거부됨")
                }
                return
            }
        }
    }

}


activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
 
 
    <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" tools:srcCompat="@tools:sample/avatars"
            app:layout_constraintTop_toTopOf="parent" app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent"
            android:id="@+id/imageView"/>
</android.support.constraint.ConstraintLayout>



댓글