Android-এ এক্সটার্নাল স্টোরেজ (SD কার্ড) পড়া/লিখার অনুমতি এবং বাস্তবায়ন
এই উদাহরণটি দেখায় কিভাবে Android-এ এক্সটার্নাল স্টোরেজ (SD কার্ড) তে ফাইল তৈরি, লিখা এবং পড়া যায়। Android 10 (API 29) থেকে Scoped Storage চালু হওয়ায় এক্সটার্নাল স্টোরেজ এক্সেসের নিয়ম উল্লেখযোগ্যভাবে পরিবর্তন হয়েছে।
📌 গুরুত্বপূর্ণ: Android 10+ (API 29+) এ
WRITE_EXTERNAL_STORAGE পারমিশন থাকলেও সাধারণ ফোল্ডার অ্যাক্সেস সীমাবদ্ধ। অ্যাপ-স্পেসিফিক ডিরেক্টরি (getExternalFilesDir()) ব্যবহার করুন যা কোনো পারমিশন ছাড়াই অ্যাক্সেসযোগ্য। পাবলিক ফোল্ডার (Downloads, Documents ইত্যাদি) অ্যাক্সেসের জন্য MediaStore বা Storage Access Framework (ACTION_OPEN_DOCUMENT_TREE) ব্যবহার করুন।
প্রয়োজনীয় ধাপসমূহ
ধাপ ১: নতুন প্রজেক্ট তৈরি করুন
Android Studio-তে File → New Project এ যান এবং প্রজেক্ট তৈরি করুন।
ধাপ ২: লেআউট ফাইল (res/layout/activity_main.xml)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
xmlns:tools="https://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="12dp"
tools:context=".MainActivity">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="এক্সটার্নাল স্টোরেজে পড়া এবং লিখা"
android:textSize="24sp"
android:textStyle="bold"
android:textAlignment="center" />
<EditText
android:id="@+id/myInputText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="30dp"
android:ems="10"
android:gravity="top|start"
android:lines="5"
android:minLines="3"
android:hint="এখানে টেক্সট লিখুন..." />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:orientation="horizontal"
android:weightSum="1.0">
<Button
android:id="@+id/saveExternalStorage"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.5"
android:text="সংরক্ষণ" />
<Button
android:id="@+id/getExternalStorage"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.5"
android:text="পড়ুন" />
</LinearLayout>
<TextView
android:id="@+id/response"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:padding="5dp"
android:text=""
android:textAppearance="?android:attr/textAppearanceMedium" />
</LinearLayout>
ধাপ ৩: MainActivity.java বাস্তবায়ন করুন
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.os.Environment;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
public class MainActivity extends AppCompatActivity {
EditText editText;
TextView textView;
Button saveButton, readButton;
File myExternalFile;
String myData = "";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
editText = findViewById(R.id.myInputText);
textView = findViewById(R.id.response);
saveButton = findViewById(R.id.saveExternalStorage);
readButton = findViewById(R.id.getExternalStorage);
// সংরক্ষণ বাটন ক্লিক লিসেনার
saveButton.setOnClickListener(v -> {
try {
FileOutputStream fos = new FileOutputStream(myExternalFile);
fos.write(editText.getText().toString().getBytes());
fos.close();
editText.setText("");
textView.setText("SampleFile.txt এক্সটার্নাল স্টোরেজে সংরক্ষিত হয়েছে...");
} catch (IOException e) {
e.printStackTrace();
textView.setText("ত্রুটি: " + e.getMessage());
}
});
// পড়ার বাটন ক্লিক লিসেনার
readButton.setOnClickListener(v -> {
try {
FileInputStream fis = new FileInputStream(myExternalFile);
DataInputStream in = new DataInputStream(fis);
BufferedReader br = new BufferedReader(new InputStreamReader(in));
String strLine;
myData = "";
while ((strLine = br.readLine()) != null) {
myData += strLine + "\n";
}
in.close();
editText.setText(myData);
textView.setText("SampleFile.txt ডেটা পড়া হয়েছে...");
} catch (IOException e) {
e.printStackTrace();
textView.setText("ত্রুটি: " + e.getMessage());
}
});
// স্টোরেজ অবস্থা চেক করুন
if (!isExternalStorageAvailable() || isExternalStorageReadOnly()) {
saveButton.setEnabled(false);
textView.setText("এক্সটার্নাল স্টোরেজ উপলব্ধ নয় বা রিড-অনলি মোডে");
} else {
// অ্যাপ-স্পেসিফিক ডিরেক্টরি ব্যবহার করুন (Scoped Storage-র জন্য নিরাপদ)
String fileName = "SampleFile.txt";
String filePath = "MyFolder";
myExternalFile = new File(getExternalFilesDir(filePath), fileName);
}
}
// এক্সটার্নাল স্টোরেজ রিড-অনলি কিনা চেক করুন
private static boolean isExternalStorageReadOnly() {
String extStorageState = Environment.getExternalStorageState();
return Environment.MEDIA_MOUNTED_READ_ONLY.equals(extStorageState);
}
// এক্সটার্নাল স্টোরেজ উপলব্ধ কিনা চেক করুন
private static boolean isExternalStorageAvailable() {
String extStorageState = Environment.getExternalStorageState();
return Environment.MEDIA_MOUNTED.equals(extStorageState);
}
}
ধাপ ৪: AndroidManifest.xml-এ পারমিশন যোগ করুন
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="https://schemas.android.com/apk/res/android"
package="app.com.sample">
<!-- Android 9 (API 28) এবং তার নিচের জন্য -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="28" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="28" />
<!-- Android 10+ (API 29+) জন্যScoped Storage-এ অ্যাপ-স্পেসিফিক ডিরেক্টরির জন্য কোনো পারমিশন লাগে না -->
<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"
android:requestLegacyExternalStorage="true"> <!-- Android 10-এ লিগ্যাসি স্টোরেজ সক্ষম করতে (অস্থায়ী) -->
<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>
Android ভার্সন অনুযায়ী স্টোরেজ নীতি
| Android ভার্সন | API লেভেল | স্টোরেজ মডেল | পারমিশন প্রয়োজন |
|---|---|---|---|
| Android 9 এবং নিচে | <= 28 | Legacy Storage | WRITE_EXTERNAL_STORAGE, READ_EXTERNAL_STORAGE |
| Android 10 (Q) | 29 | Scoped Storage (ঐচ্ছিক) | অ্যাপ-স্পেসিফিকের জন্য কোনো পারমিশন নয়; লিগ্যাসি 위해 android:requestLegacyExternalStorage="true" |
| Android 11 (R) + | >= 30 | Scoped Storage (বাধ্যতামূলক) | অ্যাপ-স্পেসিফিকের জন্য কোনো পারমিশন নয়; পাবলিক ফাইল জন্য MediaStore / SAF |
আধুনিক সেরা অনুশীলন (Best Practices)
১. অ্যাপ-স্পেসিফিক ফাইল (কোনো পারমিশন লাগে না)
// ফাইল লিখা File file = new File(getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS), "myfile.txt"); // ফাইল পড়া File file = new File(getExternalFilesDir(null), "myfile.txt");
২. MediaStore API (পাবলিক মিডিয়া ফাইল - ছবি, ভিডিও, অডিও)
ContentValues values = new ContentValues();
values.put(MediaStore.MediaColumns.DISPLAY_NAME, "image.jpg");
values.put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg");
values.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES + "/MyApp");
Uri uri = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
try (OutputStream os = getContentResolver().openOutputStream(uri)) {
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, os);
}
৩. Storage Access Framework (ডকুমেন্ট/অন্যান্য ফাইল)
// ফাইল তৈরি করার জন্য
Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("text/plain");
intent.putExtra(Intent.EXTRA_TITLE, "notes.txt");
startActivityForResult(intent, CREATE_FILE_REQUEST);
অ্যাপ্লিকেশন চালানোর পদ্ধতি
আপনার Android ডিভাইস কম্পিউটারের সাথে সংযুক্ত করুন। Android Studio-তে Run আইকনে ক্লিক করুন এবং ডিভাইসে আউটপুট দেখুন।

