⚠️ 2021.01.14에 작성된 글입니다 ⚠️
코드는 [Android] 이미지 받아 다른 액티비티에 전달하기에서 이어집니다.
파이어베이스에 사진을 업로드하기 위해선 안드로이드 프로젝트와 파이어베이스가 연결되어야 합니다.
자세한 방법은 [Android] Firebase와 안드로이드 프로젝트 연결하기에서 확인할 수 있습니다.
코드 흐름
업로드 버튼 클릭
↓
firebase에 업로드하는 메소드(uploadImg()) 실행
↓
업로드 성공시 업로드한 이미지 uri다운받는 메소드(downloadUri()) 실행
↓
다운받은 이미지 Uri로 다른 액티비티에 이미지 표시하기
uploadImg와 downloadUri메소드 실행 중 로딩메시지(prograssDialog)도 보여줌
파이어베이스 저장소
1. Firebase Storage 다운로드
AndroidStudio>Tools>Firebase>Storage>Add Cloud Storage to your app
2. Firebase Storage 권한(규칙) 수정
firebase>프로젝트>Storage>Rules
권한 없어도 읽기 및 쓰기 가능하도록 수정
rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
allow read, write //: if request.auth != null;
}
}
}
화면
업로드 버튼 추가
- activity_get_image.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".GetImageActivity">
<ImageView
android:id="@+id/iv_main"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal">
<Button
android:id="@+id/btn_camera"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="3dp"
android:layout_weight="1"
android:text="@string/camera" />
<Button
android:id="@+id/btn_gallery"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="3dp"
android:layout_weight="1"
android:text="@string/gallery" />
<Button
android:id="@+id/btn_move"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="3dp"
android:layout_weight="1"
android:text="@string/move" />
<Button
android:id="@+id/btn_upload"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="3dp"
android:layout_weight="1"
android:text="@string/upload" />
</LinearLayout>
</LinearLayout>
업로드 동작
- GetImageActivity.java
업로드 버튼 클릭 이벤트
public class GetImageActivity extends AppCompatActivity implements View.OnClickListener {
...
Button btnUpload;
...
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_get_image);
...
btnUpload = findViewById(R.id.btn_upload);
btnUpload.setOnClickListener(this);
...
}
@Override
public void onClick(View view) {
switch (view.getId()) {
...
case R.id.btn_upload: // 업로드 선택 시
if (imagePath.length() > 0 && imgFrom >= 100) {
uploadImg(); // 업로드 작업 실행
}
break;
}
}
uploadImg() 메소드 : 파일을 파이어베이스 저장소(Firebase Storage)에 올리기
void uploadImg() {
// firebase storage 에 이미지 업로드하는 method
showProgressDialog("업로드 중");
UploadTask uploadTask = null; // 파일 업로드하는 객체
switch (imgFrom) {
case GALLERY:
/*갤러리 선택 시 새로운 파일명 생성 후 reference 에 경로 세팅,
* uploadTask 에서 onActivityResult()에서 받은 인텐트의 데이터(Uri)를 업로드하기로 설정*/
String timeStamp = imageDate.format(new Date()); // 중복 파일명을 막기 위한 시간스탬프
String imageFileName = "IMAGE_" + timeStamp + "_.png"; // 파일명
reference = storage.getReference().child("item").child(imageFileName); // 이미지 파일 경로 지정 (/item/imageFileName)
uploadTask = reference.putFile(imageUri); // 업로드할 파일과 업로드할 위치 설정
break;
case CAMERA:
/*카메라 선택 시 생성했던 이미지파일명으로 reference 에 경로 세팅,
* uploadTask 에서 생성한 이미지파일을 업로드하기로 설정*/
reference = storage.getReference().child("item").child(imageFile.getName()); // imageFile.toString()을 할 경우 해당 파일의 경로 자체가 불러와짐
uploadTask = reference.putFile(Uri.fromFile(imageFile)); // 업로드할 파일과 업로드할 위치 설정
break;
}
// 파일 업로드 시작
uploadTask.addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() {
@Override
public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) {
// 업로드 성공 시 동작
hideProgressDialog();
Log.d(TAG, "onSuccess: upload");
downloadUri(); // 업로드 성공 시 업로드한 파일 Uri 다운받기
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception e) {
// 업로드 실패 시 동작
hideProgressDialog();
Log.d(TAG, "onFailure: upload");
}
});
}
downloadUri() 메소드 : 업로드한 파일의 uri 값 다운로드하기
void downloadUri() {
// 지정한 경로(reference)에 대한 uri 을 다운로드하는 method
showProgressDialog("다운로드 중");
reference.getDownloadUrl().addOnSuccessListener(new OnSuccessListener<Uri>() {
@Override
public void onSuccess(Uri uri) {
// uri 다운로드 성공 시 동작
// 다운받은 uri를 인텐트에 넣어 다른 액티비티로 이동
hideProgressDialog();
Log.d(TAG, "onSuccess: download");
intent = new Intent(GetImageActivity.this, SetImageActivity.class);
intent.putExtra("path", uri.toString()); // 다운로드한 uri, String 형으로 바꿔 인텐트에 넣기
startActivity(intent);
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception e) {
// uri 다운로드 실패 시 동작
hideProgressDialog();
Log.d(TAG, "onFailure: download");
}
});
}
progressDialog 메소드 : progressDialog 보여주거나, 그만 보여주게 하는 메소드
ProgressDialog mProgressDialog;
public void showProgressDialog(String message) {
if (mProgressDialog == null) {
mProgressDialog = new ProgressDialog(this);
mProgressDialog.setMessage(message);
mProgressDialog.setIndeterminate(true);
}
mProgressDialog.show();
}
public void hideProgressDialog() {
if (mProgressDialog != null && mProgressDialog.isShowing()) {
mProgressDialog.dismiss();
}
}
결과 화면
1. 카메라로 사진 업로드
2. 갤러리로 사진 업로드
3. Firebase Storage Files
firebase>프로젝트>Storage>Files
파이어베이스 저장소에 업로드한 이미지 파일이 잘 올라간 것을 확인할 수 있다.
전체 코드
- manifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.uploadimage">
<!--카메라 및 저장위치를 위치 접근을 위한 권한 설정-->
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<!--앱을 사용하기 위한 조건 required 값에 따라 반드시 필요한 속성일 수도, 없어도 일단 실행은 되는 속성일 수도 있음-->
<uses-feature
android:name="android.hardware.camera"
android:required="false" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.UploadImage">
<!--카메라앱으로부터 이미지를 받기 위한 content provider 추가
androidx package 내 FileProvider 이용 (특정 폴더 공유할 때 사용하는 content provider-->
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.example.uploadimage.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/external" />
</provider>
<activity android:name=".GetImageActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".SetImageActivity" />
</application>
</manifest>
- gradle (Module: ...)
plugins {
id 'com.android.application'
id 'com.google.gms.google-services'
}
android {
compileSdkVersion 30
buildToolsVersion "30.0.2"
defaultConfig {
applicationId "com.example.uploadimage"
minSdkVersion 24
targetSdkVersion 30
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'com.google.android.material:material:1.2.1'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation 'com.github.bumptech.glide:glide:4.11.0'
implementation 'com.google.firebase:firebase-storage:19.2.1'
testImplementation 'junit:junit:4.13.1'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
}
- activity_get_image.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".GetImageActivity">
<ImageView
android:id="@+id/iv_main"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal">
<Button
android:id="@+id/btn_camera"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="3dp"
android:layout_weight="1"
android:text="@string/camera" />
<Button
android:id="@+id/btn_gallery"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="3dp"
android:layout_weight="1"
android:text="@string/gallery" />
<Button
android:id="@+id/btn_move"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="3dp"
android:layout_weight="1"
android:text="@string/move" />
<Button
android:id="@+id/btn_upload"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="3dp"
android:layout_weight="1"
android:text="@string/upload" />
</LinearLayout>
</LinearLayout>
- GetImageActivity.java
package com.example.uploadimage;
import android.Manifest;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.FileProvider;
import com.bumptech.glide.Glide;
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.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class GetImageActivity extends AppCompatActivity implements View.OnClickListener {
final int CAMERA = 100; // 카메라 선택시 인텐트로 보내는 값
final int GALLERY = 101; // 갤러리 선택 시 인텐트로 보내는 값
int imgFrom; // 이미지 어디서 가져왔는지 (카메라 or 갤러리)
String imagePath = "";
String TAG = "@@TAG@@";
@SuppressLint("SimpleDateFormat")
SimpleDateFormat imageDate = new SimpleDateFormat("yyyyMMdd_HHmmss");
Intent intent;
ImageView imageView;
Button btnCamera, btnGallery, btnMove, btnUpload;
ProgressDialog mProgressDialog;
File imageFile = null; // 카메라 선택 시 새로 생성하는 파일 객체
Uri imageUri = null;
FirebaseStorage storage = FirebaseStorage.getInstance(); // 파이어베이스 저장소 객체
StorageReference reference = null; // 저장소 레퍼런스 객체 : storage 를 사용해 저장 위치를 설정
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_get_image);
imageView = findViewById(R.id.iv_main);
btnCamera = findViewById(R.id.btn_camera);
btnGallery = findViewById(R.id.btn_gallery);
btnMove = findViewById(R.id.btn_move);
btnUpload = findViewById(R.id.btn_upload);
btnCamera.setOnClickListener(this);
btnGallery.setOnClickListener(this);
btnMove.setOnClickListener(this);
btnUpload.setOnClickListener(this);
// 권한 체크
boolean hasCamPerm = checkSelfPermission(Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED;
boolean hasWritePerm = checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;
if (!hasCamPerm || !hasWritePerm) // 권한 없을 시 권한설정 요청
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
}
@SuppressLint({"NonConstantResourceId", "QueryPermissionsNeeded"})
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.btn_camera: // 카메라 선택 시
intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (intent.resolveActivity(getPackageManager()) != null) {
try {
imageFile = createImageFile();
} catch (IOException e) {
e.printStackTrace();
}
if (imageFile != null) {
Uri imageUri = FileProvider.getUriForFile(getApplicationContext(),
"com.example.uploadimage.fileprovider",
imageFile);
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
startActivityForResult(intent, CAMERA);
}
}
break;
case R.id.btn_gallery: // 갤러리 선택 시
intent = new Intent(Intent.ACTION_PICK);
intent.setType(MediaStore.Images.Media.CONTENT_TYPE);
intent.setType("image/*");
startActivityForResult(intent, GALLERY);
break;
case R.id.btn_move: // 이동 선택 시
if (imagePath.length() > 0) { // 이미지 경로가 있을 경우
intent = new Intent(getApplicationContext(), SetImageActivity.class);
intent.putExtra("path", imagePath);
startActivity(intent);
}
break;
case R.id.btn_upload: // 업로드 선택 시
if (imagePath.length() > 0 && imgFrom >= 100) {
uploadImg(); // 업로드 작업 실행
}
break;
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == Activity.RESULT_OK) { // 결과가 있을 경우
// 갤러리를 선택한 경우 인텐트를 활용해 이미지 정보 가져오기
if (requestCode == GALLERY) { // 갤러리 선택한 경우
imageUri = data.getData(); // 이미지 Uri 정보
imagePath = data.getDataString(); // 이미지 위치 경로 정보
}
/* 카메라를 선택할 경우, createImageFile()에서 별도의 imageFile 을 생성 및 파일 절대경로 저장을 하기 때문에
onActivityResult()에서는 별도의 작업 필요無 */
// 저장한 파일 경로를 이미지 라이브러리인 Glide 사용하여 이미지 뷰에 세팅하기
if (imagePath.length() > 0) {
Glide.with(this)
.load(imagePath)
.into(imageView);
imgFrom = requestCode; // 사진을 가져온 곳이 카메라일 경우 CAMERA(100), 갤러리일 경우 GALLERY(101)
}
}
}
@SuppressLint("SimpleDateFormat")
File createImageFile() throws IOException {
// 이미지 파일 생성
String timeStamp = imageDate.format(new Date()); // 파일명 중복을 피하기 위한 "yyyyMMdd_HHmmss"꼴의 timeStamp
String fileName = "IMAGE_" + timeStamp; // 이미지 파일 명
File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
File file = File.createTempFile(fileName,
".jpg",
storageDir); // 이미지 파일 생성
imagePath = file.getAbsolutePath(); // 파일 절대경로 저장하기
return file;
}
void uploadImg() {
// firebase storage 에 이미지 업로드하는 method
showProgressDialog("업로드 중");
UploadTask uploadTask = null; // 파일 업로드하는 객체
switch (imgFrom) {
case GALLERY:
/*갤러리 선택 시 새로운 파일명 생성 후 reference 에 경로 세팅,
* uploadTask 에서 onActivityResult()에서 받은 인텐트의 데이터(Uri)를 업로드하기로 설정*/
String timeStamp = imageDate.format(new Date()); // 중복 파일명을 막기 위한 시간스탬프
String imageFileName = "IMAGE_" + timeStamp + "_.png"; // 파일명
reference = storage.getReference().child("item").child(imageFileName); // 이미지 파일 경로 지정 (/item/imageFileName)
uploadTask = reference.putFile(imageUri); // 업로드할 파일과 업로드할 위치 설정
break;
case CAMERA:
/*카메라 선택 시 생성했던 이미지파일명으로 reference 에 경로 세팅,
* uploadTask 에서 생성한 이미지파일을 업로드하기로 설정*/
reference = storage.getReference().child("item").child(imageFile.getName()); // imageFile.toString()을 할 경우 해당 파일의 경로 자체가 불러와짐
uploadTask = reference.putFile(Uri.fromFile(imageFile)); // 업로드할 파일과 업로드할 위치 설정
break;
}
// 파일 업로드 시작
uploadTask.addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() {
@Override
public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) {
// 업로드 성공 시 동작
hideProgressDialog();
Log.d(TAG, "onSuccess: upload");
downloadUri(); // 업로드 성공 시 업로드한 파일 Uri 다운받기
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception e) {
// 업로드 실패 시 동작
hideProgressDialog();
Log.d(TAG, "onFailure: upload");
}
});
}
void downloadUri() {
// 지정한 경로(reference)에 대한 uri 을 다운로드하는 method
showProgressDialog("다운로드 중");
reference.getDownloadUrl().addOnSuccessListener(new OnSuccessListener<Uri>() {
@Override
public void onSuccess(Uri uri) {
// uri 다운로드 성공 시 동작
// 다운받은 uri를 인텐트에 넣어 다른 액티비티로 이동
hideProgressDialog();
Log.d(TAG, "onSuccess: download");
intent = new Intent(GetImageActivity.this, SetImageActivity.class);
intent.putExtra("path", uri.toString()); // 다운로드한 uri, String 형으로 바꿔 인텐트에 넣기
startActivity(intent);
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception e) {
// uri 다운로드 실패 시 동작
hideProgressDialog();
Log.d(TAG, "onFailure: download");
}
});
}
public void showProgressDialog(String message) {
if (mProgressDialog == null) {
mProgressDialog = new ProgressDialog(this);
mProgressDialog.setMessage(message);
mProgressDialog.setIndeterminate(true);
}
mProgressDialog.show();
}
public void hideProgressDialog() {
if (mProgressDialog != null && mProgressDialog.isShowing()) {
mProgressDialog.dismiss();
}
}
}
- activity_set_image.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".SetImageActivity">
<TextView
android:id="@+id/tv_image_path"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:padding="10dp"
android:textColor="@color/black"
android:textSize="20sp" />
<ImageView
android:id="@+id/iv_image"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
</LinearLayout>
- SetImageActivity.java
package com.example.uploadimage;
import android.os.Bundle;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import com.bumptech.glide.Glide;
public class SetImageActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_set_image);
TextView textView = findViewById(R.id.tv_image_path);
ImageView imageView = findViewById(R.id.iv_image);
String imagePath = getIntent().getStringExtra("path");
textView.setText(imagePath);
Glide.with(this).load(imagePath).into(imageView);
}
}
참고자료
안드로이드 코틀린 : Firebase Storage 이미지 저장 :: 랴파파의 콜렉션 (tistory.com)
Android에서 Cloud Storage 시작하기 | Firebase (google.com)
Android에서 파일 다운로드 | Firebase (google.com)
Android에서 Cloud Storage 시작하기 | Firebase (google.com)
Default FirebaseApp is not initialized 오류 발생시 (kentakang.com)
android app에서 firebase storage의 데이터 가져오기 | by platfarm tech team | platfarm | Medium
공부하며 정리한 글입니다. 내용에 대한 피드백은 언제나 환영입니다.
'Android' 카테고리의 다른 글
[Android] Thread - 텍스트 변경하기 (0) | 2022.10.23 |
---|---|
[Android] Thread - 화면 변경하기(초시계) (0) | 2022.10.22 |
[Android] Firebase와 안드로이드 프로젝트 연결하기 (0) | 2022.10.22 |
[Android] Glide 사용하기 (0) | 2022.10.22 |
[Android] 이미지 받아 다른 액티비티에 전달하기 (0) | 2022.10.22 |