শেয়ার্ড স্টোরেজ থেকে নথি এবং অন্যান্য ফাইল অ্যাক্সেস করুন

অ্যান্ড্রয়েড ৪.৪ (এপিআই লেভেল ১৯) এবং তার উচ্চতর সংস্করণে চালিত ডিভাইসগুলিতে, আপনার অ্যাপ স্টোরেজ অ্যাক্সেস ফ্রেমওয়ার্ক ব্যবহার করে এক্সটার্নাল স্টোরেজ ভলিউম এবং ক্লাউড-ভিত্তিক স্টোরেজ সহ একটি ডকুমেন্টস প্রোভাইডারের সাথে ইন্টারঅ্যাক্ট করতে পারে। এই ফ্রেমওয়ার্কটি ব্যবহারকারীদের একটি সিস্টেম পিকারের মাধ্যমে ডকুমেন্টস প্রোভাইডার বেছে নিতে এবং আপনার অ্যাপের তৈরি, খোলা বা পরিবর্তন করার জন্য নির্দিষ্ট ডকুমেন্ট ও অন্যান্য ফাইল নির্বাচন করতে দেয়।

যেহেতু আপনার অ্যাপ কোন ফাইল বা ডিরেক্টরিগুলো অ্যাক্সেস করতে পারবে তা ব্যবহারকারীই নির্বাচন করেন, তাই এই পদ্ধতিতে কোনো সিস্টেম পারমিশনের প্রয়োজন হয় না এবং এতে ব্যবহারকারীর নিয়ন্ত্রণ ও গোপনীয়তা বৃদ্ধি পায়। এছাড়াও, এই ফাইলগুলো, যা কোনো অ্যাপ-নির্দিষ্ট ডিরেক্টরি এবং মিডিয়া স্টোরের বাইরে সংরক্ষিত থাকে, আপনার অ্যাপ আনইনস্টল করার পরেও ডিভাইসে থেকে যায়।

ফ্রেমওয়ার্কটি ব্যবহার করার জন্য নিম্নলিখিত ধাপগুলো অনুসরণ করতে হয়:

  1. একটি অ্যাপ এমন একটি ইন্টেন্ট চালু করে, যেটিতে স্টোরেজ-সম্পর্কিত একটি অ্যাকশন থাকে। এই অ্যাকশনটি ফ্রেমওয়ার্ক দ্বারা উপলব্ধ একটি নির্দিষ্ট ইউজ কেসের সাথে সঙ্গতিপূর্ণ।
  2. ব্যবহারকারী একটি সিস্টেম পিকার দেখতে পান, যা তাকে একটি ডকুমেন্টস প্রোভাইডার ব্রাউজ করতে এবং এমন একটি অবস্থান বা ডকুমেন্ট বেছে নিতে দেয় যেখানে সংরক্ষণ-সম্পর্কিত কাজটি সম্পন্ন হবে।
  3. অ্যাপটি ব্যবহারকারীর নির্বাচিত অবস্থান বা ডকুমেন্টকে প্রতিনিধিত্বকারী একটি URI-তে পঠন ও লিখন অ্যাক্সেস লাভ করে। এই URI ব্যবহার করে, অ্যাপটি নির্বাচিত অবস্থানে বিভিন্ন অপারেশন সম্পাদন করতে পারে।

Android 9 (API লেভেল 28) বা তার নিচের সংস্করণে চালিত ডিভাইসগুলিতে মিডিয়া ফাইল অ্যাক্সেস সমর্থন করার জন্য, READ_EXTERNAL_STORAGE পারমিশনটি ঘোষণা করুন এবং maxSdkVersion কে 28 এ সেট করুন।

এই নির্দেশিকাটি ফাইল এবং অন্যান্য নথি নিয়ে কাজ করার জন্য ফ্রেমওয়ার্কটির সমর্থিত বিভিন্ন ব্যবহারের ক্ষেত্র ব্যাখ্যা করে। এটি ব্যবহারকারীর নির্বাচিত স্থানে কীভাবে বিভিন্ন অপারেশন সম্পাদন করতে হয়, তাও ব্যাখ্যা করে।

ডকুমেন্ট এবং অন্যান্য ফাইল অ্যাক্সেস করার ব্যবহারিক ক্ষেত্র

স্টোরেজ অ্যাক্সেস ফ্রেমওয়ার্ক ফাইল এবং অন্যান্য নথি অ্যাক্সেস করার জন্য নিম্নলিখিত ব্যবহারের ক্ষেত্রগুলো সমর্থন করে।

একটি নতুন ফাইল তৈরি করুন
ACTION_CREATE_DOCUMENT ইন্টেন্ট অ্যাকশনটি ব্যবহারকারীদের একটি নির্দিষ্ট স্থানে ফাইল সংরক্ষণ করার সুযোগ দেয়।
একটি নথি বা ফাইল খুলুন
ACTION_OPEN_DOCUMENT ইন্টেন্ট অ্যাকশনটি ব্যবহারকারীদের খোলার জন্য একটি নির্দিষ্ট ডকুমেন্ট বা ফাইল নির্বাচন করার সুযোগ দেয়।
একটি ডিরেক্টরির বিষয়বস্তুতে প্রবেশাধিকার দিন
অ্যান্ড্রয়েড ৫.০ (এপিআই লেভেল ২১) এবং তার পরবর্তী সংস্করণগুলোতে উপলব্ধ ACTION_OPEN_DOCUMENT_TREE ইন্টেন্ট অ্যাকশনটি ব্যবহারকারীদের একটি নির্দিষ্ট ডিরেক্টরি নির্বাচন করার সুযোগ দেয়, যার ফলে আপনার অ্যাপ সেই ডিরেক্টরির ভেতরের সমস্ত ফাইল এবং সাব-ডিরেক্টরিগুলোতে অ্যাক্সেস পায়।

নিম্নলিখিত বিভাগগুলিতে প্রতিটি ব্যবহারের ক্ষেত্র কীভাবে কনফিগার করতে হবে সে সম্পর্কে নির্দেশনা দেওয়া হয়েছে।

একটি নতুন ফাইল তৈরি করুন

সিস্টেম ফাইল পিকার লোড করতে এবং ব্যবহারকারীকে ফাইলের বিষয়বস্তু লেখার জন্য একটি স্থান বেছে নেওয়ার সুযোগ দিতে ACTION_CREATE_DOCUMENT ইন্টেন্ট অ্যাকশনটি ব্যবহার করুন। এই প্রক্রিয়াটি অন্যান্য অপারেটিং সিস্টেমের ব্যবহৃত 'সেভ অ্যাজ' ডায়ালগগুলোর প্রক্রিয়ার অনুরূপ।

দ্রষ্টব্য: ACTION_CREATE_DOCUMENT কোনো বিদ্যমান ফাইলকে ওভাররাইট করতে পারে না। যদি আপনার অ্যাপ একই নামে একটি ফাইল সংরক্ষণ করার চেষ্টা করে, তাহলে সিস্টেম ফাইলের নামের শেষে বন্ধনীর মধ্যে একটি সংখ্যা যুক্ত করে দেয়।

উদাহরণস্বরূপ, যদি আপনার অ্যাপ এমন একটি ডিরেক্টরিতে confirmation.pdf নামের একটি ফাইল সেভ করার চেষ্টা করে যেখানে ইতিমধ্যেই সেই নামের একটি ফাইল আছে, তাহলে সিস্টেম নতুন ফাইলটি confirmation(1).pdf নামে সেভ করে।

ইনটেন্টটি কনফিগার করার সময়, ফাইলের নাম এবং MIME টাইপ উল্লেখ করুন, এবং ঐচ্ছিকভাবে EXTRA_INITIAL_URI ইনটেন্ট এক্সট্রা ব্যবহার করে ফাইল বা ডিরেক্টরির URI উল্লেখ করুন, যা ফাইল পিকারটি প্রথম লোড হওয়ার সময় প্রদর্শন করবে।

নিম্নলিখিত কোড স্নিপেটটি দেখায় কিভাবে একটি ফাইল তৈরির জন্য ইন্টেন্ট তৈরি এবং কল করতে হয়:

কোটলিন

// Request code for creating a PDF document.
const val CREATE_FILE = 1

private fun createFile(pickerInitialUri: Uri) {
    val intent = Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
        addCategory(Intent.CATEGORY_OPENABLE)
        type = "application/pdf"
        putExtra(Intent.EXTRA_TITLE, "invoice.pdf")

        // Optionally, specify a URI for the directory that should be opened in
        // the system file picker before your app creates the document.
        putExtra(DocumentsContract.EXTRA_INITIAL_URI, pickerInitialUri)
    }
    startActivityForResult(intent, CREATE_FILE)
}

জাভা

// Request code for creating a PDF document.
private static final int CREATE_FILE = 1;

private void createFile(Uri pickerInitialUri) {
    Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
    intent.addCategory(Intent.CATEGORY_OPENABLE);
    intent.setType("application/pdf");
    intent.putExtra(Intent.EXTRA_TITLE, "invoice.pdf");

    // Optionally, specify a URI for the directory that should be opened in
    // the system file picker when your app creates the document.
    intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, pickerInitialUri);

    startActivityForResult(intent, CREATE_FILE);
}

একটি ফাইল খুলুন

আপনার অ্যাপটি স্টোরেজের একক হিসেবে ডকুমেন্ট ব্যবহার করতে পারে, যেখানে ব্যবহারকারীরা এমন ডেটা প্রবেশ করান যা তারা সহকর্মীদের সাথে শেয়ার করতে বা অন্য ডকুমেন্টে ইম্পোর্ট করতে চাইতে পারেন। এর কয়েকটি উদাহরণ হলো, একজন ব্যবহারকারীর একটি প্রোডাক্টিভিটি ডকুমেন্ট খোলা অথবা EPUB ফাইল হিসেবে সেভ করা একটি বই খোলা।

এইসব ক্ষেত্রে, ACTION_OPEN_DOCUMENT ইন্টেন্টটি কল করে ব্যবহারকারীকে খোলার জন্য ফাইলটি বেছে নেওয়ার সুযোগ দিন, যা সিস্টেমের ফাইল পিকার অ্যাপটি খোলে। শুধুমাত্র আপনার অ্যাপ যে ধরনের ফাইল সমর্থন করে, সেগুলো দেখানোর জন্য একটি MIME টাইপ নির্দিষ্ট করে দিন। এছাড়াও, EXTRA_INITIAL_URI ইন্টেন্ট এক্সট্রা ব্যবহার করে আপনি ঐচ্ছিকভাবে সেই ফাইলের URI নির্দিষ্ট করে দিতে পারেন, যা ফাইল পিকারটি প্রথম লোড হওয়ার সময় প্রদর্শন করবে।

নিম্নলিখিত কোড স্নিপেটটি দেখায় কিভাবে একটি পিডিএফ ডকুমেন্ট খোলার জন্য ইন্টেন্ট তৈরি এবং কল করতে হয়:

কোটলিন

// Request code for selecting a PDF document.
const val PICK_PDF_FILE = 2

fun openFile(pickerInitialUri: Uri) {
    val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
        addCategory(Intent.CATEGORY_OPENABLE)
        type = "application/pdf"

        // Optionally, specify a URI for the file that should appear in the
        // system file picker when it loads.
        putExtra(DocumentsContract.EXTRA_INITIAL_URI, pickerInitialUri)
    }

    startActivityForResult(intent, PICK_PDF_FILE)
}

জাভা

// Request code for selecting a PDF document.
private static final int PICK_PDF_FILE = 2;

private void openFile(Uri pickerInitialUri) {
    Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
    intent.addCategory(Intent.CATEGORY_OPENABLE);
    intent.setType("application/pdf");

    // Optionally, specify a URI for the file that should appear in the
    // system file picker when it loads.
    intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, pickerInitialUri);

    startActivityForResult(intent, PICK_PDF_FILE);
}

প্রবেশাধিকার সীমাবদ্ধতা

অ্যান্ড্রয়েড ১১ (এপিআই লেভেল ৩০) এবং এর পরবর্তী সংস্করণগুলোতে, ব্যবহারকারীকে নিম্নলিখিত ডিরেক্টরিগুলো থেকে স্বতন্ত্র ফাইল নির্বাচন করার অনুরোধ জানাতে আপনি ACTION_OPEN_DOCUMENT ইন্টেন্ট অ্যাকশনটি ব্যবহার করতে পারবেন না:

  • Android/data/ ডিরেক্টরি এবং এর সমস্ত সাবডিরেক্টরি।
  • Android/obb/ ডিরেক্টরি এবং এর সমস্ত সাবডিরেক্টরি।

একটি ডিরেক্টরির বিষয়বস্তুতে প্রবেশাধিকার দিন

ফাইল ম্যানেজমেন্ট এবং মিডিয়া-ক্রিয়েশন অ্যাপগুলো সাধারণত একটি ডিরেক্টরি হায়ারার্কিতে ফাইলের গ্রুপগুলো পরিচালনা করে। আপনার অ্যাপে এই সুবিধাটি দিতে, ACTION_OPEN_DOCUMENT_TREE ইন্টেন্ট অ্যাকশনটি ব্যবহার করুন, যা ব্যবহারকারীকে একটি সম্পূর্ণ ডিরেক্টরি ট্রি-তে অ্যাক্সেস দেওয়ার সুযোগ দেয়, তবে অ্যান্ড্রয়েড ১১ (এপিআই লেভেল ৩০) থেকে কিছু ব্যতিক্রম রয়েছে। এরপর আপনার অ্যাপ নির্বাচিত ডিরেক্টরি এবং এর যেকোনো সাব-ডিরেক্টরির যেকোনো ফাইল অ্যাক্সেস করতে পারবে।

ACTION_OPEN_DOCUMENT_TREE ব্যবহার করার সময়, আপনার অ্যাপ শুধুমাত্র ব্যবহারকারীর নির্বাচিত ডিরেক্টরির ফাইলগুলোতেই অ্যাক্সেস পায়। এই ব্যবহারকারী-নির্বাচিত ডিরেক্টরির বাইরে থাকা অন্য অ্যাপের ফাইলগুলোতে আপনার কোনো অ্যাক্সেস থাকে না। এই ব্যবহারকারী-নিয়ন্ত্রিত অ্যাক্সেস ব্যবহারকারীদেরকে তাদের পছন্দ অনুযায়ী কন্টেন্ট আপনার অ্যাপের সাথে শেয়ার করার সুযোগ দেয়।

ঐচ্ছিকভাবে, আপনি EXTRA_INITIAL_URI ইন্টেন্ট এক্সট্রা ব্যবহার করে সেই ডিরেক্টরির URI নির্দিষ্ট করতে পারেন, যা ফাইল পিকারটি প্রথম লোড হওয়ার সময় প্রদর্শন করবে।

নিম্নলিখিত কোড স্নিপেটটি দেখায় কিভাবে একটি ডিরেক্টরি খোলার জন্য ইন্টেন্ট তৈরি এবং কল করতে হয়:

কোটলিন

fun openDirectory(pickerInitialUri: Uri) {
    // Choose a directory using the system's file picker.
    val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).apply {
        // Optionally, specify a URI for the directory that should be opened in
        // the system file picker when it loads.
        putExtra(DocumentsContract.EXTRA_INITIAL_URI, pickerInitialUri)
    }

    startActivityForResult(intent, your-request-code)
}

জাভা

public void openDirectory(Uri uriToLoad) {
    // Choose a directory using the system's file picker.
    Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);

    // Optionally, specify a URI for the directory that should be opened in
    // the system file picker when it loads.
    intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, uriToLoad);

    startActivityForResult(intent, your-request-code);
}

প্রবেশাধিকার সীমাবদ্ধতা

অ্যান্ড্রয়েড ১১ (এপিআই লেভেল ৩০) এবং এর পরবর্তী সংস্করণগুলোতে, আপনি নিম্নলিখিত ডিরেক্টরিগুলোতে অ্যাক্সেসের অনুরোধ জানাতে ACTION_OPEN_DOCUMENT_TREE ইন্টেন্ট অ্যাকশনটি ব্যবহার করতে পারবেন না:

  • অভ্যন্তরীণ স্টোরেজ ভলিউমের রুট ডিরেক্টরি।
  • প্রতিটি এসডি কার্ড ভলিউমের রুট ডিরেক্টরি, যা ডিভাইস প্রস্তুতকারক নির্ভরযোগ্য বলে মনে করে, কার্ডটি এমুলেটেড বা অপসারণযোগ্য যাই হোক না কেন। একটি নির্ভরযোগ্য ভলিউম হলো সেটি, যা একটি অ্যাপ বেশিরভাগ সময় সফলভাবে অ্যাক্সেস করতে পারে।
  • Download ডিরেক্টরি।

এছাড়াও, অ্যান্ড্রয়েড ১১ (এপিআই লেভেল ৩০) এবং এর পরবর্তী সংস্করণগুলোতে, ব্যবহারকারীকে নিম্নলিখিত ডিরেক্টরিগুলো থেকে স্বতন্ত্র ফাইল নির্বাচন করার অনুরোধ জানাতে আপনি ACTION_OPEN_DOCUMENT_TREE ইন্টেন্ট অ্যাকশনটি ব্যবহার করতে পারবেন না:

  • Android/data/ ডিরেক্টরি এবং এর সমস্ত সাবডিরেক্টরি।
  • Android/obb/ ডিরেক্টরি এবং এর সমস্ত সাবডিরেক্টরি।

নির্বাচিত স্থানে কার্যক্রম সম্পাদন করুন

ব্যবহারকারী সিস্টেমের ফাইল পিকার ব্যবহার করে কোনো ফাইল বা ডিরেক্টরি নির্বাচন করার পর, আপনি onActivityResult() ফাংশনে নিম্নলিখিত কোডটি ব্যবহার করে নির্বাচিত আইটেমটির URI পেতে পারেন:

কোটলিন

override fun onActivityResult(
        requestCode: Int, resultCode: Int, resultData: Intent?) {
    if (requestCode == your-request-code
            && resultCode == Activity.RESULT_OK) {
        // The result data contains a URI for the document or directory that
        // the user selected.
        resultData?.data?.also { uri ->
            // Perform operations on the document using its URI.
        }
    }
}

জাভা

@Override
public void onActivityResult(int requestCode, int resultCode,
        Intent resultData) {
    if (requestCode == your-request-code
            && resultCode == Activity.RESULT_OK) {
        // The result data contains a URI for the document or directory that
        // the user selected.
        Uri uri = null;
        if (resultData != null) {
            uri = resultData.getData();
            // Perform operations on the document using its URI.
        }
    }
}

নির্বাচিত আইটেমের URI-এর রেফারেন্স পাওয়ার মাধ্যমে আপনার অ্যাপ আইটেমটির উপর বিভিন্ন অপারেশন সম্পাদন করতে পারে। উদাহরণস্বরূপ, আপনি আইটেমটির মেটাডেটা অ্যাক্সেস করতে, আইটেমটি সরাসরি সম্পাদনা করতে এবং আইটেমটি মুছে ফেলতে পারেন।

নিম্নলিখিত বিভাগগুলিতে দেখানো হয়েছে কীভাবে ব্যবহারকারীর নির্বাচিত ফাইলগুলির উপর কাজগুলি সম্পন্ন করতে হয়।

প্রদানকারী যে কার্যক্রমগুলো সমর্থন করে তা নির্ধারণ করুন

বিভিন্ন কন্টেন্ট প্রোভাইডার ডকুমেন্টের উপর বিভিন্ন অপারেশন সম্পাদনের সুযোগ দেয়—যেমন ডকুমেন্ট কপি করা বা ডকুমেন্টের থাম্বনেইল দেখা। কোনো নির্দিষ্ট প্রোভাইডার কোন কোন অপারেশন সমর্থন করে তা জানতে, Document.COLUMN_FLAGS এর মান পরীক্ষা করুন। এরপর আপনার অ্যাপের UI শুধুমাত্র সেই অপশনগুলোই দেখাতে পারবে যা প্রোভাইডারটি সমর্থন করে।

অনুমতি বজায় রাখুন

যখন আপনার অ্যাপ পড়া বা লেখার জন্য কোনো ফাইল খোলে, তখন সিস্টেম সেই ফাইলের জন্য আপনার অ্যাপকে একটি URI পারমিশন দেয়, যা ব্যবহারকারীর ডিভাইস রিস্টার্ট না হওয়া পর্যন্ত স্থায়ী থাকে। কিন্তু ধরুন, আপনার অ্যাপটি একটি ইমেজ-এডিটিং অ্যাপ, এবং আপনি চান যে ব্যবহারকারীরা তাদের সর্বশেষ এডিট করা ৫টি ছবি সরাসরি আপনার অ্যাপ থেকেই অ্যাক্সেস করতে পারুক। যদি ব্যবহারকারীর ডিভাইস রিস্টার্ট হয়ে যায়, তাহলে ফাইলগুলো খুঁজে পাওয়ার জন্য আপনাকে ব্যবহারকারীকে আবার সিস্টেম পিকার-এ ফেরত পাঠাতে হবে।

ডিভাইস রিস্টার্টের পরেও ফাইলগুলিতে অ্যাক্সেস বজায় রাখতে এবং ব্যবহারকারীর অভিজ্ঞতা আরও উন্নত করতে, আপনার অ্যাপ সিস্টেমের দেওয়া পার্সিস্টেবল ইউআরআই পারমিশন গ্রান্টটি "টেক" করতে পারে, যেমনটি নিম্নলিখিত কোড স্নিপেটে দেখানো হয়েছে:

কোটলিন

val contentResolver = applicationContext.contentResolver

val takeFlags: Int = Intent.FLAG_GRANT_READ_URI_PERMISSION or
        Intent.FLAG_GRANT_WRITE_URI_PERMISSION
// Check for the freshest data.
contentResolver.takePersistableUriPermission(uri, takeFlags)

জাভা

final int takeFlags = intent.getFlags()
            & (Intent.FLAG_GRANT_READ_URI_PERMISSION
            | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
// Check for the freshest data.
getContentResolver().takePersistableUriPermission(uri, takeFlags);

নথির মেটাডেটা পরীক্ষা করুন

যখন আপনার কাছে কোনো ডকুমেন্টের URI থাকে, তখন আপনি সেটির মেটাডেটা অ্যাক্সেস করতে পারেন। এই কোড স্নিপেটটি URI দ্বারা নির্দিষ্ট একটি ডকুমেন্টের মেটাডেটা সংগ্রহ করে এবং তা লগ করে:

কোটলিন

val contentResolver = applicationContext.contentResolver

fun dumpImageMetaData(uri: Uri) {

    // The query, because it only applies to a single document, returns only
    // one row. There's no need to filter, sort, or select fields,
    // because we want all fields for one document.
    val cursor: Cursor? = contentResolver.query(
            uri, null, null, null, null, null)

    cursor?.use {
        // moveToFirst() returns false if the cursor has 0 rows. Very handy for
        // "if there's anything to look at, look at it" conditionals.
        if (it.moveToFirst()) {

            // Note it's called "Display Name". This is
            // provider-specific, and might not necessarily be the file name.
            val displayName: String =
                    it.getString(it.getColumnIndex(OpenableColumns.DISPLAY_NAME))
            Log.i(TAG, "Display Name: $displayName")

            val sizeIndex: Int = it.getColumnIndex(OpenableColumns.SIZE)
            // If the size is unknown, the value stored is null. But because an
            // int can't be null, the behavior is implementation-specific,
            // and unpredictable. So as
            // a rule, check if it's null before assigning to an int. This will
            // happen often: The storage API allows for remote files, whose
            // size might not be locally known.
            val size: String = if (!it.isNull(sizeIndex)) {
                // Technically the column stores an int, but cursor.getString()
                // will do the conversion automatically.
                it.getString(sizeIndex)
            } else {
                "Unknown"
            }
            Log.i(TAG, "Size: $size")
        }
    }
}

জাভা

public void dumpImageMetaData(Uri uri) {

    // The query, because it only applies to a single document, returns only
    // one row. There's no need to filter, sort, or select fields,
    // because we want all fields for one document.
    Cursor cursor = getActivity().getContentResolver()
            .query(uri, null, null, null, null, null);

    try {
        // moveToFirst() returns false if the cursor has 0 rows. Very handy for
        // "if there's anything to look at, look at it" conditionals.
        if (cursor != null && cursor.moveToFirst()) {

            // Note it's called "Display Name". This is
            // provider-specific, and might not necessarily be the file name.
            String displayName = cursor.getString(
                    cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
            Log.i(TAG, "Display Name: " + displayName);

            int sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE);
            // If the size is unknown, the value stored is null. But because an
            // int can't be null, the behavior is implementation-specific,
            // and unpredictable. So as
            // a rule, check if it's null before assigning to an int. This will
            // happen often: The storage API allows for remote files, whose
            // size might not be locally known.
            String size = null;
            if (!cursor.isNull(sizeIndex)) {
                // Technically the column stores an int, but cursor.getString()
                // will do the conversion automatically.
                size = cursor.getString(sizeIndex);
            } else {
                size = "Unknown";
            }
            Log.i(TAG, "Size: " + size);
        }
    } finally {
        cursor.close();
    }
}

একটি নথি খুলুন

কোনো ডকুমেন্টের URI-এর রেফারেন্স থাকলে, আপনি পরবর্তী প্রক্রিয়াকরণের জন্য ডকুমেন্টটি খুলতে পারেন। এই বিভাগে একটি বিটম্যাপ এবং একটি ইনপুট স্ট্রিম খোলার উদাহরণ দেখানো হয়েছে।

বিটম্যাপ

নিম্নলিখিত কোড স্নিপেটটি দেখায় কিভাবে একটি Bitmap ফাইলের URI ব্যবহার করে সেটি খোলা যায়:

কোটলিন

val contentResolver = applicationContext.contentResolver

@Throws(IOException::class)
private fun getBitmapFromUri(uri: Uri): Bitmap {
    val parcelFileDescriptor: ParcelFileDescriptor =
            contentResolver.openFileDescriptor(uri, "r")
    val fileDescriptor: FileDescriptor = parcelFileDescriptor.fileDescriptor
    val image: Bitmap = BitmapFactory.decodeFileDescriptor(fileDescriptor)
    parcelFileDescriptor.close()
    return image
}

জাভা

private Bitmap getBitmapFromUri(Uri uri) throws IOException {
    ParcelFileDescriptor parcelFileDescriptor =
            getContentResolver().openFileDescriptor(uri, "r");
    FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();
    Bitmap image = BitmapFactory.decodeFileDescriptor(fileDescriptor);
    parcelFileDescriptor.close();
    return image;
}

বিটম্যাপটি খোলার পর, আপনি এটি একটি ImageView তে প্রদর্শন করতে পারেন।

ইনপুট স্ট্রিম

নিম্নলিখিত কোড স্নিপেটটি দেখায় কিভাবে একটি URI ব্যবহার করে একটি InputStream অবজেক্ট খোলা যায়। এই স্নিপেটটিতে, ফাইলের লাইনগুলো একটি স্ট্রিং-এ পড়া হচ্ছে:

কোটলিন

val contentResolver = applicationContext.contentResolver

@Throws(IOException::class)
private fun readTextFromUri(uri: Uri): String {
    val stringBuilder = StringBuilder()
    contentResolver.openInputStream(uri)?.use { inputStream ->
        BufferedReader(InputStreamReader(inputStream)).use { reader ->
            var line: String? = reader.readLine()
            while (line != null) {
                stringBuilder.append(line)
                line = reader.readLine()
            }
        }
    }
    return stringBuilder.toString()
}

জাভা

private String readTextFromUri(Uri uri) throws IOException {
    StringBuilder stringBuilder = new StringBuilder();
    try (InputStream inputStream =
            getContentResolver().openInputStream(uri);
            BufferedReader reader = new BufferedReader(
            new InputStreamReader(Objects.requireNonNull(inputStream)))) {
        String line;
        while ((line = reader.readLine()) != null) {
            stringBuilder.append(line);
        }
    }
    return stringBuilder.toString();
}

একটি নথি সম্পাদনা করুন

আপনি স্টোরেজ অ্যাক্সেস ফ্রেমওয়ার্ক ব্যবহার করে একটি টেক্সট ডকুমেন্টকে তার স্থানেই সম্পাদনা করতে পারেন।

নিম্নলিখিত কোড স্নিপেটটি প্রদত্ত URI দ্বারা উপস্থাপিত ডকুমেন্টের বিষয়বস্তু ওভাররাইট করে:

কোটলিন

val contentResolver = applicationContext.contentResolver

private fun alterDocument(uri: Uri) {
    try {
        contentResolver.openFileDescriptor(uri, "w")?.use {
            FileOutputStream(it.fileDescriptor).use {
                it.write(
                    ("Overwritten at ${System.currentTimeMillis()}\n")
                        .toByteArray()
                )
            }
        }
    } catch (e: FileNotFoundException) {
        e.printStackTrace()
    } catch (e: IOException) {
        e.printStackTrace()
    }
}

জাভা

private void alterDocument(Uri uri) {
    try {
        ParcelFileDescriptor pfd = getActivity().getContentResolver().
                openFileDescriptor(uri, "w");
        FileOutputStream fileOutputStream =
                new FileOutputStream(pfd.getFileDescriptor());
        fileOutputStream.write(("Overwritten at " + System.currentTimeMillis() +
                "\n").getBytes());
        // Let the document provider know you're done by closing the stream.
        fileOutputStream.close();
        pfd.close();
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

একটি নথি মুছে ফেলুন

যদি আপনার কাছে কোনো ডকুমেন্টের URI থাকে এবং ডকুমেন্টটির Document.COLUMN_FLAGSSUPPORTS_DELETE থাকে, তাহলে আপনি ডকুমেন্টটি ডিলিট করতে পারবেন। উদাহরণস্বরূপ:

কোটলিন

DocumentsContract.deleteDocument(applicationContext.contentResolver, uri)

জাভা

DocumentsContract.deleteDocument(applicationContext.contentResolver, uri);

একটি সমতুল্য মিডিয়া ইউআরআই পুনরুদ্ধার করুন

getMediaUri() মেথডটি একটি মিডিয়া স্টোর URI প্রদান করে যা প্রদত্ত ডকুমেন্টস প্রোভাইডার URI-এর সমতুল্য। এই দুটি URI একই অন্তর্নিহিত আইটেমকে নির্দেশ করে। মিডিয়া স্টোর URI ব্যবহার করে, আপনি শেয়ার্ড স্টোরেজ থেকে মিডিয়া ফাইলগুলো আরও সহজে অ্যাক্সেস করতে পারেন।

getMediaUri() মেথডটি ExternalStorageProvider URI সমর্থন করে। Android 12 (API লেভেল 31) এবং এর পরবর্তী সংস্করণগুলোতে, এই মেথডটি MediaDocumentsProvider URI-ও সমর্থন করে।

একটি ভার্চুয়াল ফাইল খুলুন

অ্যান্ড্রয়েড ৭.০ (এপিআই লেভেল ২৫) এবং এর পরবর্তী সংস্করণগুলোতে, আপনার অ্যাপ স্টোরেজ অ্যাক্সেস ফ্রেমওয়ার্ক দ্বারা উপলব্ধ ভার্চুয়াল ফাইলগুলো ব্যবহার করতে পারে। যদিও ভার্চুয়াল ফাইলগুলোর কোনো বাইনারি রূপ নেই, আপনার অ্যাপ সেগুলোকে অন্য কোনো ফাইল টাইপে রূপান্তর করে অথবা ACTION_VIEW ইন্টেন্ট অ্যাকশন ব্যবহার করে সেগুলোর বিষয়বস্তু খুলতে পারে।

ভার্চুয়াল ফাইল খোলার জন্য, আপনার ক্লায়েন্ট অ্যাপে সেগুলোকে পরিচালনা করার বিশেষ লজিক অন্তর্ভুক্ত করতে হবে। উদাহরণস্বরূপ, ফাইলটি প্রিভিউ করার জন্য যদি আপনি ফাইলটির বাইট রিপ্রেজেন্টেশন পেতে চান, তাহলে আপনাকে ডকুমেন্টস প্রোভাইডারের কাছে একটি বিকল্প MIME টাইপের জন্য অনুরোধ করতে হবে।

ব্যবহারকারী নির্বাচন করার পর, ফাইলটি ভার্চুয়াল কিনা তা নির্ধারণ করতে ফলাফলের ডেটাতে থাকা URI-টি ব্যবহার করুন, যেমনটি নিম্নলিখিত কোড স্নিপেটে দেখানো হয়েছে:

কোটলিন

private fun isVirtualFile(uri: Uri): Boolean {
    if (!DocumentsContract.isDocumentUri(this, uri)) {
        return false
    }

    val cursor: Cursor? = contentResolver.query(
            uri,
            arrayOf(DocumentsContract.Document.COLUMN_FLAGS),
            null,
            null,
            null
    )

    val flags: Int = cursor?.use {
        if (cursor.moveToFirst()) {
            cursor.getInt(0)
        } else {
            0
        }
    } ?: 0

    return flags and DocumentsContract.Document.FLAG_VIRTUAL_DOCUMENT != 0
}

জাভা

private boolean isVirtualFile(Uri uri) {
    if (!DocumentsContract.isDocumentUri(this, uri)) {
        return false;
    }

    Cursor cursor = getContentResolver().query(
        uri,
        new String[] { DocumentsContract.Document.COLUMN_FLAGS },
        null, null, null);

    int flags = 0;
    if (cursor.moveToFirst()) {
        flags = cursor.getInt(0);
    }
    cursor.close();

    return (flags & DocumentsContract.Document.FLAG_VIRTUAL_DOCUMENT) != 0;
}

ডকুমেন্টটি একটি ভার্চুয়াল ফাইল কিনা তা যাচাই করার পর, আপনি ফাইলটিকে একটি বিকল্প MIME টাইপে, যেমন "image/png" , রূপান্তর করতে পারেন। নিম্নলিখিত কোড স্নিপেটটি দেখায় কিভাবে পরীক্ষা করতে হয় যে একটি ভার্চুয়াল ফাইলকে ইমেজ হিসেবে উপস্থাপন করা যায় কিনা, এবং যদি করা যায়, তাহলে ভার্চুয়াল ফাইলটি থেকে একটি ইনপুট স্ট্রিম সংগ্রহ করতে হয়:

কোটলিন

@Throws(IOException::class)
private fun getInputStreamForVirtualFile(
        uri: Uri, mimeTypeFilter: String): InputStream {

    val openableMimeTypes: Array<String>? =
            contentResolver.getStreamTypes(uri, mimeTypeFilter)

    return if (openableMimeTypes?.isNotEmpty() == true) {
        contentResolver
                .openTypedAssetFileDescriptor(uri, openableMimeTypes[0], null)
                .createInputStream()
    } else {
        throw FileNotFoundException()
    }
}

জাভা

private InputStream getInputStreamForVirtualFile(Uri uri, String mimeTypeFilter)
    throws IOException {

    ContentResolver resolver = getContentResolver();

    String[] openableMimeTypes = resolver.getStreamTypes(uri, mimeTypeFilter);

    if (openableMimeTypes == null ||
        openableMimeTypes.length < 1) {
        throw new FileNotFoundException();
    }

    return resolver
        .openTypedAssetFileDescriptor(uri, openableMimeTypes[0], null)
        .createInputStream();
}

অতিরিক্ত সম্পদ

ডকুমেন্ট ও অন্যান্য ফাইল কীভাবে সংরক্ষণ এবং অ্যাক্সেস করতে হয় সে সম্পর্কে আরও তথ্যের জন্য, নিম্নলিখিত রিসোর্সগুলো দেখুন।

নমুনা

ভিডিও