Today's

길을 나서지 않으면 그 길에서 만날 수 있는 사람을 만날 수 없다

모바일 앱(안드로이드)

안드로이드 앱 개발 하기 firebase 에 이미지를 저장해 보자...

Billcorea 2021. 10. 14. 23:46
반응형

앱 개발중에는 간혹적으로 이미지 을 불러다가 사용해야 하는 경우들이 종종 발생한다.  SQLite 을 이용할 때는 이미지를 byte 형태로 변환한 다음 저장하는 방식으로 이미지를 저장하고 불러오는 기능을 구현 했다.

 

    public long insertDayinfo(String mDate, String msg, byte[] image) {
        long _id = -1  ;
        ContentValues values = new ContentValues() ;
        values.put("mdate", mDate);
        values.put("msg", msg) ;
        values.put("image", image);
        _id = db.insert(tableName, null, values) ;
        Log.i(TAG, "insert " + _id + " " + image.toString()) ;
        return _id ;
    }

    public byte[] getByteArrayFromBitmap(Bitmap d) {
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        d.compress(Bitmap.CompressFormat.PNG, 100, stream);
        return stream.toByteArray() ;
    }

    public Bitmap getAppBitmap(byte[] b) {
        return BitmapFactory.decodeByteArray(b, 0, b.length);
    }

이미지를 불러와서 SQLite 에 insert 을 할 때 위의 함수를 호출하는 방식으로 insert 을 하는 것이였다.

Bundle bundle = data.getExtras();
                assert bundle != null;
                Bitmap test = bundle.getParcelable("data");
                binding.imageView2.setImageBitmap(test);

                dbHandler = DBHandler.open(CalendarView.this);
                long lId = dbHandler.insertDayinfo(StringUtil.getDateString(pDate), "", dbHandler.getByteArrayFromBitmap(test));
                if (lId > -1) {
                    Toast.makeText(getApplicationContext(), "저장이 되었습니다.", Toast.LENGTH_LONG).show();
                }
                dbHandler.close();

이런식의 호출이 되었지 않을까 싶다.

 

그럼 이번에는 firebase의 realtime database 을 사용하면서 경험한 부분에 대해서 이야기를 해 보자.  쉽게 생각해서 위의 예시와 같이 imageview 에 들어 있는 것을 byte[] 형식으로 변환한 다음 저장을 하면 되리라... 실제 코딩도 그렇게 해 보았더니...아니나 다를까 오류가 발생했다... 지금의 소스는 수정이 되어 버린 상황이라... 발생했던 오류 코드만 찾아 보겠다.

 

com.google.firebase.database.DatabaseException: Serializing Arrays is not supported, please use Lists instead ...

 

firebase 는 Serializing arrays 을 지원하지 않는단다... 그래서 Lists 로 구현을 하라는...  처음에는 이게 뭔 소리인가 하는 생각으로 구굴링을 해 보았는데, 결국 byte 뿐만 아니라 array 형태의 데이터 형식은 지원을 하지 않는다는 것을 알게 되었다... 

 

그래서 다음과 같은 data 구조체를 선언하였다.

import com.billcoreatech.dailylike1010.utils.StringUtil;
import java.util.ArrayList;
import java.util.Arrays;

public class MemberBean {
    String memberId;
    String name ;
    ...
    
    String subscriptionDate ;
    String withdrawalDate ;
    String imgMyInfo ; //  사진 이미지

    public void setImgMyInfo(String imgMyInfo) {
        this.imgMyInfo = imgMyInfo;
    }

    public String getImgMyInfo() {
        return this.imgMyInfo ;
    }

    ....

    public String getUserType() {
        return userType;
    }
}

뭐 그냥 쉬운 string 으로만 구현 했다...  그럼 이제 화면의 이미지는 어떻게 저장을 할 것인가 ?

 

앱에 imageview 을 넣고 그안에 그림이 들어가는 activity을 구현하고  폰에서 저장된 이미지를 가져오고

그것을 저장하기 위해서 

 

이제 어떻게 ?

 

 

 

 

 

 

/**
     * byte[] 을 string 으로
     * @param b
     * @return
     */
    public static String byteArrayToBinaryString(byte[] b) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < b.length; ++i) {
            sb.append(byteToBinaryString(b[i]));
        }
        return sb.toString();
    }

    public static String byteToBinaryString(byte n) {
        StringBuilder sb = new StringBuilder("00000000");
        for (int bit = 0; bit < 8; bit++) {
            if (((n >> bit) & 1) > 0) {
                sb.setCharAt(7 - bit, '1');
            }
        }
        return sb.toString();
    }

    /**
     * string 을 byte[] 로
     * @param s
     * @return
     */
    public static byte[] binaryStringToByteArray(String s) {
        int count = s.length() / 8;
        byte[] b = new byte[count];
        for (int i = 1; i < count; ++i) {
            String t = s.substring((i - 1) * 8, i * 8); b[i - 1] = binaryStringToByte(t);
        } return b;
    }

    public static byte binaryStringToByte(String s) {
        byte ret = 0, total = 0;
        for (int i = 0; i < 8; ++i) {
            ret = (s.charAt(7 - i) == '1') ? (byte) (1 << i) : 0;
            total = (byte) (ret | total);
        }
        return total;
    }

    /**
     * drawable to string
     * @param image
     * @return
     */
    public static String drawableToString(Drawable image) {
        Bitmap bitmap = ((BitmapDrawable) image).getBitmap();
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        bitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream);
        byte[] reviewImage = stream.toByteArray();
        return byteArrayToBinaryString(reviewImage);
    }

    /**
     * drawable to string
     * @param imgMyInfo
     * @return
     */
    public static Drawable stringToDrawable(String imgMyInfo) {
        byte[] bData = binaryStringToByteArray(imgMyInfo);
        ByteArrayInputStream is = new ByteArrayInputStream(bData);
        return Drawable.createFromStream(is, "reviewImage");
    }

이런 몇가지 함수를 class 에 넣었다... 그것 중에서 활용하는 방식은 다음과 같다.

옆을 들어 위에 함수들이 들어있는 class 가 StringUtil.java 라고 한다면 ...

    memberDb.orderByChild("emailAddress").equalTo(mUser.getEmail()).addListenerForSingleValueEvent(new ValueEventListener() {
            @Override
            public void onDataChange(@NonNull DataSnapshot snapshot) {
                for(DataSnapshot ds : snapshot.getChildren()) {
                    mMemberBean = ds.getValue(MemberBean.class);
                    if (mMemberBean != null) {
                        binding.txtEmail.setText(mMemberBean.getEmailAddress());
                        
                        ...
                        
                        if (mMemberBean.getImgMyInfo() == null) {
                            binding.imgMyinfo.setImageResource(R.drawable.image_512);
                        } else {
                            // 불러온 이미지를 화면의 이미지뷰에 넣기...
                            binding.imgMyinfo.setImageDrawable(StringUtil.stringToDrawable(mMemberBean.getImgMyInfo()));
                        }
                    }
                }
            }

            @Override
            public void onCancelled(@NonNull DatabaseError error) {

            }
        });
        
        
  ...
  
  
  Bundle bundle = data.getExtras();
  assert bundle != null;
  Bitmap test = bundle.getParcelable("data");
  binding.imgMyinfo.setImageBitmap(test);
  // 이미지뷰에서 이미지를 읽어와서 저장하기 위한 변수에 넣기
  mMemberBean.setImgMyInfo(StringUtil.drawableToString(binding.imgMyinfo.getDrawable()));

작업한 소스의 일부만 켭쳐한 것이기는 하지만, 함수 하나로 쓩~ 이미지를 변수에 넣고 뺴고가 가능해 진다.

 

그럼 실제 화면에는 

 

 이렇게 보이게 될 것이고.

 

 

 

 

 

 

 

 

 

 

firebase 의 데이터에는 어떻게 ?

imgMyInfo 란 변수에는 1과 0으로 구성된 숫자만 가득.... 

 

 

 

 

이렇게 해서 firebase 의 realtime database 을 활용하는 작업을 할 때에도 이미지 처리는 무난(?) 하게 할 수 있게 되었다.

 

반응형