블로그 이사했습니다!
👇 블로그 이전 공지 👇
👇 새 블로그에서 글 보기 👇
[Android] Firbase 사진 업로드하기 — Win Record (tistory.com)
[Android] Firbase 사진 업로드하기
⚠️ 2021.01.14에 작성된 글입니다 ⚠️ 코드는 [Android] 이미지 받아 다른 액티비티에 전달하기에서 이어집니다. 파이어베이스에 사진을 업로드하기 위해선 안드로이드 프로젝트와 파이어베이스
win-record.tistory.com
코드는 '이미지 가져와 다른 액티비티에 세팅하기' 글에서 이어집니다.
파이어베이스에 사진을 업로드하기 위해선 안드로이드 프로젝트와 파이어베이스가 연결되어야 합니다.
자세한 방법은 파이어베이스 연결 관련 글에서 확인할 수 있습니다.
코드 흐름
업로드 버튼 클릭
↓
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 관련 : Firebase와 안드로이드 프로젝트 연결하기 (tistory.com)
이미지 관련 : 이미지 받아 다른 액티비티에 전달하기 (tistory.com)
다음 글
다음 글 예정
참고자료
안드로이드 코틀린 : 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' 카테고리의 다른 글
SharedPreferences 객체 저장하기 (0) | 2021.01.16 |
---|---|
SharedPreferences 사용해 데이터 저장하기 (0) | 2021.01.15 |
이미지 받아 다른 액티비티에 전달하기 (0) | 2021.01.14 |
가져온 이미지 회전 막기 (0) | 2021.01.14 |
Glide 사용하기 (0) | 2021.01.14 |
댓글