[Android&Firebase] 파이어베이스의 파이어스토리지에 이미지 저장

2021. 6. 3. 13:12Android

※ 해당 포스터는 파이어 베이스 내부에서 제공해주는 파이어 스토리지에 이미지를 저장하는 방식을 담고 있습니다.

※ 안드로이드 스튜디오와 파이어베이스 연동은 >>여기<< 를 참고해주세요.

※ 해당 포스터에서 사용한 안드로이드 SDK 버전은 29이며, buildToolsVersion은 29.0.3을 사용했습니다.

※ 실행 화면 ※

스토리지에 업로드 됨

 

 

 

1. 파이어 베이스 프로젝트에 있는 스토리지 활성화

1) Firebase 프로젝트 클릭 → 왼쪽 메뉴 바의 Storage 클릭

 

2) 시작하기 클릭

 

3) 안내창 내용 읽고 다음 → 완료 클릭 

 

4) 스토리지 활성화 후 Rules 클릭

 

5) Rules을 아래 내용으로 반드시 수정

rules_version = '2';
service firebase.storage {
  match /b/{bucket}/o {
    match /{allPaths=**} {
      allow read, write: if true;
    }
  }
}

▶ 해당 규칙은 스토리지에 모든 사용자가 이미지를 올리거나 읽을 수 있게 허락하는 규칙입니다. 해당 규칙으로 변경하지 않을 경우 파이어 스토리지에 이미지를 업로드하는 과정에서 Permission denied 에러가 발생할 수 있습니다.

 

 

 

2. 이미지가 보일 화면 xml를 생성합니다.

activity_img.xml 디자인

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:layout_marginTop="100dp"
        android:gravity="center_horizontal"
        android:orientation="vertical"
        android:padding="10px">


        <androidx.cardview.widget.CardView
            android:layout_width="400dp"
            android:layout_height="400dp" >
            
            <ImageView
                android:id="@+id/profileImg"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:src="@drawable/ic_baseline_person_24" />
        </androidx.cardview.widget.CardView>
        
    </LinearLayout>

</LinearLayout>

app:src : xml 리소스(이미지 파일)를 화면에 출력하기 위해 사용, src에 리소스 id를 기입해 불러옴 

  • 안드로이드에서 이미지 파일을 출력하기 위해서는 프로젝트 안에 drawable 폴더에 이미지를 넣어야 한다.
  • drawable경로 = 안드로이드 프로젝트 폴더\app\src\app\src\main\res\drawable
  • 안드로이드에서 제공하는 이미지 리소스 파일 불러와서 사용하는 법은 더보기를 눌러서 확인해주세요.
더보기

① res → drawable 왼쪽 클릭 → New → Vector Asset 클릭

 

② Clip Art 클릭해서 필요한 모양 찾기

(Color = 아이콘을 칠할 색상 변경 // Opacity : 아이콘 투명도 조절(숫자가 적을수록 투명해짐))

 

③ 원하는 이미지 선택 후 색상 및 투명도 조절까지 끝나면 Next 클릭

 

④ Finish 누르면 끝



 

3. 갤러리 접근 권한을 얻기 위해 Manifest.xml에 권한 요청 코드 추가

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

 

 

 

4. 파이어스토어를 사용하기 위해 build.gradle(:app)에 implementation 추가

implementation platform('com.google.firebase:firebase-bom:26.5.0')
implementation 'com.google.firebase:firebase-storage'

▶ dependencies 안에 코드 작성 후 반드시 오른쪽 상단에 있는 Sync Now 클릭!

 

 

 

6. Activity 파일 생성

▼ 전체 코드 ▼

package com.example.firebasetest;

import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.view.View;
import android.widget.ImageView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import com.google.android.gms.tasks.OnFailureListener;
import com.google.android.gms.tasks.OnSuccessListener;
import com.google.firebase.storage.FirebaseStorage;
import com.google.firebase.storage.StorageReference;
import com.google.firebase.storage.UploadTask;
import java.io.InputStream;

public class ImgActivity extends AppCompatActivity {
    private  final int GALLERY_CODE = 10;
    ImageView photo;
    private FirebaseStorage storage;

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_img);
        findViewById(R.id.Img).setOnClickListener(onClickListener);
        photo=(ImageView)findViewById(R.id.Img);
        storage=FirebaseStorage.getInstance();
    }

    View.OnClickListener onClickListener = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            switch (v.getId()){
                case R.id.Img:
                    loadAlbum();
                    break;
            }
        }
    };

    private void loadAlbum(){
        Intent intent = new Intent(Intent.ACTION_PICK);
        intent.setType(MediaStore.Images.Media.CONTENT_TYPE);
        startActivityForResult(intent, GALLERY_CODE);
    }

    @Override
    protected void onActivityResult(int requestCode, final int resultCode, final Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == GALLERY_CODE) {
            Uri file = data.getData();
            StorageReference storageRef = storage.getReference();
            StorageReference riversRef = storageRef.child("photo/1.png");
            UploadTask uploadTask = riversRef.putFile(file);
            
            try {
                InputStream in = getContentResolver().openInputStream(data.getData());
                Bitmap img = BitmapFactory.decodeStream(in);
                in.close();
                photo.setImageBitmap(img);
            } catch (Exception e) {
                e.printStackTrace();
            }

            uploadTask.addOnFailureListener(new OnFailureListener() {
                @Override
                public void onFailure(@NonNull Exception exception) {
                    Toast.makeText(ImgActivity.this, "사진이 정상적으로 업로드 되지 않았습니다." ,Toast.LENGTH_SHORT).show();
                }
            }).addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() {
                @Override
                public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) {
                    Toast.makeText(ImgActivity.this, "사진이 정상적으로 업로드 되었습니다." ,Toast.LENGTH_SHORT).show();
                }
            });
        }
    }
}

 

▼ 부분 코드 및 설명▼

public class ImgActivity extends AppCompatActivity {
    private  final int GALLERY_CODE = 10;
    ImageView photo;
    private FirebaseStorage storage;

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_img);
        findViewById(R.id.Img).setOnClickListener(onClickListener);
        photo=(ImageView)findViewById(R.id.Img);
        storage=FirebaseStorage.getInstance();
    }
    //생략
 }

private final int GALLERY_CODE : 휴대폰 갤러리에 접근하기 위해 선언한 코드 번호

 private FirebaseStorage storage : 파이어 스토리지에 접근하기 위해 사용

 storage=FirebaseStorage.getInstance() : 스토리지에 접근하기 위한 인스턴스 선언

 

    View.OnClickListener onClickListener = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            switch (v.getId()){
                case R.id.Img:
                    loadAlbum();
                    break;
            }
        }
    };

    private void loadAlbum(){
        Intent intent = new Intent(Intent.ACTION_PICK);
        intent.setType(MediaStore.Images.Media.CONTENT_TYPE);
        startActivityForResult(intent, GALLERY_CODE);
    }

 View.OnClickListener onClickListener~ : 카드 뷰 안에 있는 이미지 뷰를 클릭하면 loadAlbum이 실행되도록 함

 loadAlbum() : 앱에서 휴대폰 갤러리를 실행할 수 있도록 해주는 함수

 

protected void onActivityResult(int requestCode, final int resultCode, final Intent data) {
      super.onActivityResult(requestCode, resultCode, data);
      if (requestCode == GALLERY_CODE) {
          Uri file = data.getData();
          StorageReference storageRef = storage.getReference();
          StorageReference riversRef = storageRef.child("photo/1.png");
          UploadTask uploadTask = riversRef.putFile(file);

          try {
             InputStream in = getContentResolver().openInputStream(data.getData());
             Bitmap img = BitmapFactory.decodeStream(in);
             in.close();
             photo.setImageBitmap(img);
          } catch (Exception e) {
             e.printStackTrace();
          }

          uploadTask.addOnFailureListener(new OnFailureListener() {
              @Override
              public void onFailure(@NonNull Exception exception) {
                  Toast.makeText(ImgActivity.this, "사진이 정상적으로 업로드 되지 않았습니다." ,Toast.LENGTH_SHORT).show();
              }
            }).addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() {
             @Override
             public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) {
                 Toast.makeText(ImgActivity.this, "사진이 정상적으로 업로드 되었습니다." ,Toast.LENGTH_SHORT).show();
              }
          });
       }
  }

onActivityResult() : 갤러리에서 사용자가 선택한 사진을 파이어 스토어에 올라가도록 하는 함수

 StorageReference riversRef = storageRef.child : ( ) 안에는 이미지가 저장될 경로를 적는다. 본문의 예제 코드인 "photo/1.png" 같은 경우, 스토리지에 photo 폴더에 1.png 이라는 이름으로 사진이 저장되도록 되어있다.

이때, 경로 안에 있는 폴더가 스토리지에 존재하지 않으면 스토리지에서 자체적으로 폴더를 생성하여 경로에 맞게 저장해준다.

(이때 파일 이름을 동적으로 설정하지 않고 본문의 코드처럼 정적으로 설정할 경우, 새로운 이미지 파일을 업로드 시 기존에 있던 이미지 파일에 덮어써지는 문제가 발생한다.)

 try ~ : 선택한 이미지를 비트맵으로 생성하여 처리하는 부분

 uploadTask.addOnFailureListener : 스토리지에 정상적으로 이미지 파일을 업로드할 수 없을 때 아래 있는 코드 수행 (본문의 코드 같은 경우 Toast를 사용하여 토스 메시지를 띄우게 했다.)

 addOnSuccessListener : 스토리지에 정상적으로 이미지 파일이 업로드되었을 때 아래 있는 코드 수행 (본문의 코드 같은 경우 Toast를 사용하여 토스 메시지를 띄우게 했다.)