Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Android

วิธีแปลง Base64 String เป็น Bitmap ใน Android

ภาพรวม

บทความนี้สาธิตวิธีแปลงสตริง Base64 กลับเป็น Bitmap ใน Android ซึ่งเป็นกระบวนการย้อนกลับของการ Encode ใช้เมื่อ: รับข้อมูลรูปจาก API/Server, อ่านจาก Database/SharedPreferences, หรือประมวลผลภาพที่ส่งมาในรูปแบบ String

ขั้นตอนที่ 1: สร้างโปรเจกต์ใหม่

สร้างโปรเจกต์ Android ใหม่ใน Android Studio

ขั้นตอนที่ 2: Layout (res/layout/activity_main.xml)

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center_horizontal"
    android:orientation="vertical"
    android:padding="16dp">

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="250dp"
        android:layout_height="250dp"
        android:scaleType="centerCrop"
        android:contentDescription="Decoded Image" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:text="Base64 → Bitmap Demo"
        android:textSize="16sp"
        android:textStyle="bold|italic" />

    <Button
        android:id="@+id/btnLoadFromString"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:text="โหลดจาก Base64 String" />

</LinearLayout>

ขั้นตอนที่ 3: MainActivity.java - ครอบคลุม Encode + Decode

import androidx.appcompat.app.AppCompatActivity;
import android.annotation.SuppressLint;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.util.Base64;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import java.io.ByteArrayOutputStream;

public class MainActivity extends AppCompatActivity {

    private ImageView imageView;
    private TextView tvBase64Preview;
    private Button btnLoadFromString;
    private String cachedBase64String;

    @SuppressLint("WrongThread")
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        imageView = findViewById(R.id.imageView);
        tvBase64Preview = findViewById(R.id.tvBase64Preview);
        btnLoadFromString = findViewById(R.id.btnLoadFromString);

        // 1. Encode: Bitmap → Base64 (ทำครั้งเดียวตอนเริ่มต้น)
        encodeDrawableToBase64();

        // 2. Decode: Base64 → Bitmap (เมื่อกดปุ่ม)
        btnLoadFromString.setOnClickListener(v -> decodeBase64ToBitmap());
    }

    // ==================== ENCODE: Drawable/Bitmap → Base64 ====================
    private void encodeDrawableToBase64() {
        new Thread(() -> {
            try {
                // โหลด Bitmap จาก Resources
                Bitmap originalBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.sample_image);
                
                if (originalBitmap == null) {
                    runOnUiThread(() -> Toast.makeText(this, "ไม่พบรูปภาพใน Drawable", Toast.LENGTH_SHORT).show());
                    return;
                }

                // บีบอัดเป็น JPEG (หรือ PNG)
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                originalBitmap.compress(Bitmap.CompressFormat.JPEG, 90, baos); // Quality 90%
                byte[] imageBytes = baos.toByteArray();

                // Encode เป็น Base64 String
                cachedBase64String = Base64.encodeToString(imageBytes, Base64.DEFAULT);
                
                // แสดง Preview (ตัดให้สั้น)
                runOnUiThread(() -> {
                    String preview = cachedBase64String.length() > 100 
                        ? cachedBase64String.substring(0, 100) + "..."
                        : cachedBase64String;
                    tvBase64Preview.setText("Base64 Preview:\n" + preview);
                });

            } catch (Exception e) {
                e.printStackTrace();
                runOnUiThread(() -> Toast.makeText(this, "Encode Error: " + e.getMessage(), Toast.LENGTH_SHORT).show());
            }
        }).start();
    }

    // ==================== DECODE: Base64 → Bitmap ====================
    private void decodeBase64ToBitmap() {
        if (cachedBase64String == null || cachedBase64String.isEmpty()) {
            Toast.makeText(this, "ยังไม่มี Base64 String", Toast.LENGTH_SHORT).show();
            return;
        }

        new Thread(() -> {
            try {
                // 1. Decode Base64 String → byte[]
                byte[] decodedBytes = Base64.decode(cachedBase64String, Base64.DEFAULT);

                // 2. Decode byte[] → Bitmap
                Bitmap decodedBitmap = BitmapFactory.decodeByteArray(decodedBytes, 0, decodedBytes.length);

                if (decodedBitmap != null) {
                    runOnUiThread(() -> {
                        imageView.setImageBitmap(decodedBitmap);
                        Toast.makeText(this, "Decode สำเร็จ! ขนาด: " + decodedBitmap.getWidth() + "x" + decodedBitmap.getHeight(), Toast.LENGTH_SHORT).show();
                    });
                } else {
                    runOnUiThread(() -> Toast.makeText(this, "Decode ล้มเหลว: ข้อมูลเสียหาย", Toast.LENGTH_SHORT).show());
                }

            } catch (IllegalArgumentException e) {
                runOnUiThread(() -> Toast.makeText(this, "Base64 Format ผิด: " + e.getMessage(), Toast.LENGTH_SHORT).show());
            } catch (Exception e) {
                e.printStackTrace();
                runOnUiThread(() -> Toast.makeText(this, "Error: " + e.getMessage(), Toast.LENGTH_SHORT).show());
            }
        }).start();
    }

    // ==================== Utility Methods ====================

    /**
     * แปลง Base64 String → Bitmap (Static Method สำหรับใช้งานทั่วไป)
     */
    public static Bitmap base64ToBitmap(String base64String) {
        if (base64String == null || base64String.isEmpty()) return null;
        
        try {
            // ลบ Header ถ้ามี (data:image/png;base64,)
            if (base64String.contains(",")) {
                base64String = base64String.substring(base64String.indexOf(",") + 1);
            }
            
            byte[] decodedBytes = Base64.decode(base64String, Base64.DEFAULT);
            return BitmapFactory.decodeByteArray(decodedBytes, 0, decodedBytes.length);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * แปลง Bitmap → Base64 String
     */
    public static String bitmapToBase64(Bitmap bitmap, Bitmap.CompressFormat format, int quality) {
        if (bitmap == null) return null;
        
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        bitmap.compress(format, quality, baos);
        byte[] imageBytes = baos.toByteArray();
        return Base64.encodeToString(imageBytes, Base64.DEFAULT);
    }

    /**
     * บันทึก Base64 ลง SharedPreferences
     */
    private void saveBase64ToPrefs(String key, String base64) {
        getSharedPreferences("ImagePrefs", MODE_PRIVATE)
            .edit()
            .putString(key, base64)
            .apply();
    }

    /**
     * อ่าน Base64 จาก SharedPreferences
     */
    private String loadBase64FromPrefs(String key) {
        return getSharedPreferences("ImagePrefs", MODE_PRIVATE)
            .getString(key, null);
    }
}

ขั้นตอนที่ 4: AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="https://schemas.android.com/apk/res/android"
    package="app.com.sample">

    <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/AppTheme">

        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>

รูปแบบ Base64 String ที่พบบ่อย

รูปแบบตัวอย่างการจัดการ
Raw Base64/9j/4AAQSkZJRgABAQ...Decode โดยตรง
Data URIdata:image/png;base64,/9j/4AAQ...ต้องตัดส่วนหน้าออก substring(indexOf(",")+1)
JSON Field{"image": "/9j/4AAQ..."}Parse JSON แล้ว Decode

Base64 Flags (Android)

  • Base64.DEFAULT - มาตรฐาน มี Line Wrap (76 chars)
  • Base64.NO_WRAP - ไม่มี Line Break (เหมาะสำหรับ JSON, URL, SharedPreferences) แนะนำ
  • Base64.URL_SAFE - ใช้ - และ _ แทน + และ / (URL Safe)
  • Base64.NO_PADDING - ไม่เติม = ท้ายสตริง

Best Practices

  • Thread: Encode/Decode ใน Background Thread (RxJava, Coroutine, Thread)
  • Memory: Base64 ใหญ่กว่า Binary ~33% ระวัง OOM กับรูปขนาดใหญ่
  • Compression: JPEG Quality 70-90% สมดุลระหว่าง Size/Quality
  • Validation: Try-Catch IllegalArgumentException สำหรับ Base64 ผิดรูปแบบ
  • Caching: Cache Bitmap ที่ Decode แล้ว (LruCache) หากใช้ซ้ำ

กรณีการใช้งานจริง

  • API Response: Server ส่งรูปเป็น Base64 ใน JSON
  • Offline Storage: เก็บรูปใน SQLite/Room/String SharedPreferences
  • Image Sharing: แปลงเป็น Base64 ส่งผ่าน Intent/Text
  • Signature/Canvas: วาดลายเซ็น → Bitmap → Base64 → Upload
  • QR/Barcode: Generate Bitmap → Base64 → Print/Share