অ্যান্ড্রয়েড: কোনও সামগ্রী ইউআরআই থেকে একটি ফাইল ইউআরআই পাচ্ছেন?


133

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

content://media/external/audio/media/710

তবে, জনপ্রিয় ফাইল ম্যানেজার অ্যাপ্লিকেশন অ্যাস্ট্রো ব্যবহার করে আমি নিম্নলিখিতগুলি পেয়েছি:

file:///sdcard/media/audio/ringtones/GetupGetOut.mp3

দ্বিতীয়টি আমার সাথে কাজ করার জন্য অনেক বেশি অ্যাক্সেসযোগ্য, তবে অবশ্যই আমি চাই যে অ্যাপ্লিকেশনটি তাদের সংগ্রহ ব্রাউজ করতে যে প্রোগ্রামটি ব্যবহার করে তা নির্ধারণ না করেই অডিও ফাইলটির সাথে তার কার্যকারিতা থাকতে পারে। সুতরাং আমার প্রশ্নটি হল, content://শৈলীর ইউআরআইকে ইউআরআইতে রূপান্তর করার কোনও উপায় আছে file://কি? অন্যথায়, আপনি এই সমস্যার সমাধান করার জন্য আমার কাছে কী প্রস্তাব করবেন? এখানে কোডটি যা চয়নকারীকে কল করে রেফারেন্সের জন্য:

Intent ringIntent = new Intent();
ringIntent.setType("audio/mp3");
ringIntent.setAction(Intent.ACTION_GET_CONTENT);
ringIntent.addCategory(Intent.CATEGORY_OPENABLE);
startActivityForResult(Intent.createChooser(ringIntent, "Select Ringtone"), SELECT_RINGTONE);

আমি ইউআরআই সামগ্রীর সাথে নিম্নলিখিতটি করি:

m_ringerPath = m_ringtoneUri.getPath();
File file = new File(m_ringerPath);

তারপরে সেই ফাইলটির সাথে কিছু ফাইলআইপুট স্ট্রিম স্টাফ করুন।


1
আপনি কী কলগুলি ব্যবহার করছেন যা সামগ্রী ইউআরআই পছন্দ করে না?
ফিল লেলো

1
এমন লিখিত সামগ্রীর URIs যেখানে আপনি ফাইল পাথ না পেতে পারেন, কারণ সব বিষয়বস্তু URIs অনেক আছে আছে filepaths। ফাইলপথ ব্যবহার করবেন না।
মুগিং হাঁস

উত্তর:


153

কোনও ইউআরআই থেকে একটি getContentResolver().openInputStream(uri)পেতে কেবল ব্যবহার করুন InputStream

http://developer.android.com/reference/android/content/ContentResolver.html#openInputStream(android.net.Uri)


12
ইউআরআইয়ের স্কিমটি চয়নকারী ক্রিয়াকলাপ থেকে আপনাকে ফিরে এসেছে কিনা তা পরীক্ষা করুন। যদি uri.getScheme.equals ("সামগ্রী") থাকে তবে এটি একটি বিষয়বস্তু সমাধানকারী দিয়ে খুলুন। যদি uri.Scheme.equals ("ফাইল") থাকে তবে সাধারণ ফাইল পদ্ধতি ব্যবহার করে এটি খুলুন। যে কোনও উপায়ে, আপনি এমন একটি ইনপুট স্ট্রিমটি শেষ করবেন যা আপনি সাধারণ কোড ব্যবহার করে প্রক্রিয়া করতে পারেন।
জেসন লেব্রুন

16
আসলে, আমি শুধু getContentResolver () এর জন্য দস্তাবেজ পুনরায় পড়া। OpenInputStream (), এবং এটি যাতে করে আপনি "বিষয়বস্তু" বা "ফাইল" এর স্কিম জন্য স্বয়ংক্রিয়ভাবে কাজ করে না স্কীম চেক করতে হবে ... যদি আপনি নিরাপদে পারেন ধরে নিন যে এটি সর্বদা সামগ্রী হতে পারে: // বা ফাইল: // তারপরে ওপেন ইনপুটস্ট্রিম () সর্বদা কাজ করবে।
জেসন লেব্রুন

57
ইনপুট স্ট্রিম (সামগ্রী থেকে: ...) এর পরিবর্তে ফাইল পাওয়ার কী উপায় আছে?
অ্যালিকেলজিন-কিলাকা

4
@ কিলাকা আপনি ফাইলের পথটি পেতে পারেন তবে এটি বেদনাদায়ক। দেখুন stackoverflow.com/a/20559418/294855
Danyal Aytekin

7
ফাইল স্ট্রিমের চেয়ে ফাইলগুলিতে নির্ভর করে এমন একটি ক্লোজড-সোর্স API ব্যবহার করছেন এমন ব্যক্তির পক্ষে এই উত্তরটি অপর্যাপ্ত, তবে ব্যবহারকারীকে ফাইলটি নির্বাচনের অনুমতি দেওয়ার জন্য ওএস ব্যবহার করতে চায়। @ ড্যানিয়েল আয়েটকিনের উত্তরটি আমার যা প্রয়োজন তা হ'ল (এবং বাস্তবে আমি প্রচুর পরিমাণে ফ্যাট ছাঁটাই করতে সক্ষম হয়েছি কারণ আমি জানি যে আমি কী ধরণের ফাইল নিয়ে কাজ করছি)।
monkey0506

45

কিছু নির্দিষ্ট বিষয়বস্তু সমাধানের ব্যথার পয়েন্টগুলি অতিক্রম করার অবনমিত এবং হ্যাকি উপায় সহ এটি একটি পুরানো উত্তর। এটি কয়েকটি বিশাল শস্য লবণের সাথে নিয়ে যান এবং যথাসম্ভব যথাযথ ওপেন ইনপুটস্ট্রিম API ব্যবহার করুন।

আপনি ইউআরআই file://থেকে পাথ পেতে কনটেন্ট রিসলভারটি ব্যবহার করতে পারেন content://:

String filePath = null;
Uri _uri = data.getData();
Log.d("","URI = "+ _uri);                                       
if (_uri != null && "content".equals(_uri.getScheme())) {
    Cursor cursor = this.getContentResolver().query(_uri, new String[] { android.provider.MediaStore.Images.ImageColumns.DATA }, null, null, null);
    cursor.moveToFirst();   
    filePath = cursor.getString(0);
    cursor.close();
} else {
    filePath = _uri.getPath();
}
Log.d("","Chosen path = "+ filePath);

1
ধন্যবাদ, এটি পুরোপুরি কাজ করেছে worked আমি গৃহীত উত্তরের পরামর্শ মতো একটি ইনপুটস্ট্রিম ব্যবহার করতে পারিনি।
ld

4
এই স্থানীয় ফাইল কেবল কাজ করে, যেমন এটা আছে Google ড্রাইভ জন্য কাজ না
bluewhile

1
কখনও কখনও কাজ করে, কখনও কখনও ফাইল দেয়: /// সঞ্চয়স্থান / এমুলেটেড / 0 / ... যা বিদ্যমান নেই exists
রেজা মোহাম্মদী

1
"_ডাটা" ( android.provider.MediaStore.Images.ImageColumns.DATA) কলামটি কি স্কিম থাকলে সর্বদা উপস্থিত থাকার নিশ্চয়তা দেয় content://?
এডওয়ার্ড ফালক

2
এটি একটি প্রধান অ্যান্টি-প্যাটার্ন। কিছু বিষয়বস্তু সরবরাহকারীরা এই কলামটি সরবরাহ করে তবে আপনি কন্টেন্টআরসলবারকে Fileবাইপাস করার চেষ্টা করার সময় আপনার কাছে পঠন / লেখার অ্যাক্সেসের নিশ্চয়তা নেই । content://ইউরিসে অপারেট করতে কনটেন্ট রিসলভার পদ্ধতি ব্যবহার করুন , এটি গুগল ইঞ্জিনিয়ারদের দ্বারা উত্সাহিত করা সরকারী পদ্ধতি approach
ব্যবহারকারী1643723

9

যদি আপনার সাথে একটি সামগ্রীর উরি থাকে তবে content://com.externalstorage...আপনি অ্যান্ড্রয়েড 19 বা ততোধিক সংস্করণে কোনও ফোল্ডার বা ফাইলের নিখুঁত পথ পেতে এই পদ্ধতিটি ব্যবহার করতে পারেন ।

public static String getPath(final Context context, final Uri uri) {
    final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;

    // DocumentProvider
    if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
        System.out.println("getPath() uri: " + uri.toString());
        System.out.println("getPath() uri authority: " + uri.getAuthority());
        System.out.println("getPath() uri path: " + uri.getPath());

        // ExternalStorageProvider
        if ("com.android.externalstorage.documents".equals(uri.getAuthority())) {
            final String docId = DocumentsContract.getDocumentId(uri);
            final String[] split = docId.split(":");
            final String type = split[0];
            System.out.println("getPath() docId: " + docId + ", split: " + split.length + ", type: " + type);

            // This is for checking Main Memory
            if ("primary".equalsIgnoreCase(type)) {
                if (split.length > 1) {
                    return Environment.getExternalStorageDirectory() + "/" + split[1] + "/";
                } else {
                    return Environment.getExternalStorageDirectory() + "/";
                }
                // This is for checking SD Card
            } else {
                return "storage" + "/" + docId.replace(":", "/");
            }

        }
    }
    return null;
}

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

getPath() uri: content://com.android.externalstorage.documents/tree/612E-B7BF%3A/document/612E-B7BF%3A
getPath() uri authority: com.android.externalstorage.documents
getPath() uri path: /tree/612E-B7BF:/document/612E-B7BF:
getPath() docId: 612E-B7BF:, split: 1, type: 612E-B7BF

প্রধান স্মৃতি

getPath() uri: content://com.android.externalstorage.documents/tree/primary%3A/document/primary%3A
getPath() uri authority: com.android.externalstorage.documents
getPath() uri path: /tree/primary:/document/primary:
getPath() docId: primary:, split: 1, type: primary

আপনি যদি file:///রাস্তা ব্যবহারের পরে উরি পেতে চান

DocumentFile documentFile = DocumentFile.fromFile(new File(path));
documentFile.getUri() // will return a Uri with file Uri

আমি মনে করি না এটি অ্যাপ্লিকেশনটিতে এটি করার সঠিক উপায়। দুর্ভাগ্যক্রমে আমি এটি একটি দ্রুত প্রকল্পে ব্যবহার করছি
বন্ডাক্স

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

এবং ডকুমেন্টফিল ক্লাস এপিআই 19 এ নতুন সংযোজন এবং আপনি কীভাবে সাফ থেকে ইউরিস ব্যবহার করেন use সঠিক উপায়টি হ'ল আপনার অ্যাপ্লিকেশনটির জন্য একটি স্ট্যান্ডার্ড পাথ ব্যবহার করা এবং ব্যবহারকারীকে SAF ui এর মাধ্যমে কোনও ফোল্ডারের জন্য অনুমতি দিতে বলা, উরি স্ট্রিংকে ভাগ করা পছন্দগুলিতে সংরক্ষণ করুন এবং যখন ডকুমেন্টফিল অবজেক্টগুলির সাথে ফোল্ডারে অ্যাক্সেসের প্রয়োজন হবে
থ্র্যাসিয়ান

7

কনটেন্ট: // স্কিম দিয়ে ইউআরআই হ্যান্ডেল করার চেষ্টা করা ContentResolver.query()ভাল সমাধান নয়। এইচটিসি ডিজায়ার ৪.২.২ চলছে, আপনি একটি ক্যোয়ারির ফলস্বরূপ NULL পেতে পারেন।

পরিবর্তে কন্টেন্টআরসলভার ব্যবহার করবেন না কেন? https://stackoverflow.com/a/29141800/3205334


তবে মাঝে মাঝে আমাদের কেবল পথের প্রয়োজন হয়। আমাদের আসলে ফাইলটি মেমোরিতে লোড করতে হবে না।
কিমি চিউ

2
যদি আপনার কাছে এর অ্যাক্সেসের অধিকার না থাকে তবে "পথ" অকেজো। উদাহরণস্বরূপ, যদি কোনও অ্যাপ্লিকেশন আপনাকে একটি content://ইউরি দেয় , এর ব্যক্তিগত অভ্যন্তরীণ ডিরেক্টরিতে ফাইলের সাথে সম্পর্কিত, আপনি Fileনতুন অ্যান্ড্রয়েড সংস্করণগুলিতে এপিআই সহ সেই ইউরি ব্যবহার করতে সক্ষম হবেন না । কনটেন্ট রিসোলভার এই ধরণের সুরক্ষা সীমাবদ্ধতাগুলি অতিক্রম করার জন্য ডিজাইন করা হয়েছে। আপনি যদি কন্টেন্টরেসলভার থেকে ইউরি পেয়ে থাকেন তবে আপনি এটি কেবলমাত্র কাজ করার আশা করতে পারেন।
ব্যবহারকারী1643723

5

অনুপ্রাণিত উত্তর হ'ল জেসন ল্যাব্রুন এবং ডার্থ রাভেন । ইতিমধ্যে জবাব দেওয়া পদ্ধতির চেষ্টা করে নীচের সমাধানের দিকে নিয়ে যায় যা বেশিরভাগ ক্ষেত্রে কার্সার নাল মামলা এবং বিষয়বস্তু থেকে রূপান্তর করতে পারে : // ফাইল: //

ফাইল রূপান্তর করতে, পড়া ইউরি থেকে ফাইলটি পড়ুন এবং লিখুন

public static Uri getFilePathFromUri(Uri uri) throws IOException {
    String fileName = getFileName(uri);
    File file = new File(myContext.getExternalCacheDir(), fileName);
    file.createNewFile();
    try (OutputStream outputStream = new FileOutputStream(file);
         InputStream inputStream = myContext.getContentResolver().openInputStream(uri)) {
        FileUtil.copyStream(inputStream, outputStream); //Simply reads input to output stream
        outputStream.flush();
    }
    return Uri.fromFile(file);
}

ফাইলের নাম ব্যবহারের জন্য এটি কার্সার নাল কেসটি কভার করবে

public static String getFileName(Uri uri) {
    String fileName = getFileNameFromCursor(uri);
    if (fileName == null) {
        String fileExtension = getFileExtension(uri);
        fileName = "temp_file" + (fileExtension != null ? "." + fileExtension : "");
    } else if (!fileName.contains(".")) {
        String fileExtension = getFileExtension(uri);
        fileName = fileName + "." + fileExtension;
    }
    return fileName;
}

মাইম টাইপ থেকে ফাইল এক্সটেনশনে রূপান্তর করার জন্য ভাল বিকল্প রয়েছে

 public static String getFileExtension(Uri uri) {
    String fileType = myContext.getContentResolver().getType(uri);
    return MimeTypeMap.getSingleton().getExtensionFromMimeType(fileType);
}

ফাইলের নাম পাওয়ার জন্য কার্সার

public static String getFileNameFromCursor(Uri uri) {
    Cursor fileCursor = myContext.getContentResolver().query(uri, new String[]{OpenableColumns.DISPLAY_NAME}, null, null, null);
    String fileName = null;
    if (fileCursor != null && fileCursor.moveToFirst()) {
        int cIndex = fileCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
        if (cIndex != -1) {
            fileName = fileCursor.getString(cIndex);
        }
    }
    return fileName;
}

ধন্যবাদ, এক সপ্তাহ ধরে এই দিকে তাকিয়ে আছি এর জন্য কোনও ফাইল অনুলিপি করা পছন্দ করবেন না তবে এটি কাজ করে।
justdan0227

3

ঠিক আছে আমি উত্তর দিতে কিছুটা দেরি করেছি, তবে আমার কোডটি পরীক্ষা করা হয়েছে

ইউরি থেকে স্কিম পরীক্ষা করুন:

 byte[] videoBytes;

if (uri.getScheme().equals("content")){
        InputStream iStream =   context.getContentResolver().openInputStream(uri);
            videoBytes = getBytes(iStream);
        }else{
            File file = new File(uri.getPath());
            FileInputStream fileInputStream = new FileInputStream(file);     
            videoBytes = getBytes(fileInputStream);
        }

উপরের উত্তরে আমি ভিডিও uri কে বাইট অ্যারেতে রূপান্তরিত করেছি, তবে এটি প্রশ্নের সাথে সম্পর্কিত নয়, আমি কেবলমাত্র আমার ব্যবহারটি প্রদর্শন করার জন্য আমার সম্পূর্ণ কোডটি অনুলিপি করেছি FileInputStreamএবং InputStreamউভয়ই আমার কোডে একই কাজ করছে।

আমি আমার ভগ্নাংশ এবং ক্রিয়াকলাপে getActivity () যা পরিবর্তনশীল প্রসঙ্গটি ব্যবহার করেছি এটি কেবল ActivityName.this

context=getActivity(); খণ্ডে //

context=ActivityName.this;// ক্রিয়াকলাপে


আমি জানি এটি প্রশ্নের সাথে সম্পর্কিত নয় তবে আপনি কীভাবে এটি ব্যবহার করবেন byte[] videoBytes;? বেশিরভাগ উত্তর কেবল InputStreamচিত্র সহ কীভাবে ব্যবহার করবেন তা দেখায় ।
কেআরকে
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.