سؤال

لدي بعض الملفات الكبيرة (الصور والفيديو) والتي أحتاج إلى تخزينها في مزود المحتوى. تشير وثائق Android ...

إذا كنت تعرض بيانات البايت ، فهي كبيرة جدًا بحيث لا يمكن وضعها في الجدول نفسه - مثل ملف صورة نقطية كبيرة - الحقل الذي يعرض البيانات للعملاء يجب أن يحتوي بالفعل على محتوى: uri سلسلة. هذا هو الحقل الذي يتيح للعملاء الوصول إلى ملف البيانات. يجب أن يحتوي السجل أيضًا على حقل آخر يسمى "_data" يسرد مسار الملف الدقيق على الجهاز لهذا الملف. لا يقصد من هذا الحقل بقراءته من قبل العميل ، ولكن من قبل المحتوى. سيقوم العميل باستدعاء contentResolver.openinputStream () على حقل تواجه المستخدم الذي يحمل URI للعنصر. سيطلب ContentResolver حقل "_data" لهذا السجل ، ولأنه يحتوي على أذونات أعلى من العميل ، يجب أن يكون قادرًا على الوصول إلى هذا الملف مباشرة وإرجاع غلاف قراءة للملف إلى العميل. - http://developer.android.com/guide/topics/providers/content-providers.html#creating

أواجه بعض الصعوبة في العثور على مثال. على وجه الخصوص ، أود استخدام صورة نقطية في السياق. النظر في الكود التالي شبه الكود (لا يعمل) ...

ImageView iv = ....
String iconUri = cursor.getString(cursor.getColumnIndex(Table.ICON));
iv.setImageURI(Uri.parse(iconUri));

الملاحظات/المشاكل ...

  1. كيف يمكن إعادة بناء URI المخزنة/المستردة بشكل صحيح؟ (إنه نص في الجدول)
  2. يستفيد تطبيق SetImageuri من حل المحتوى OpenInputStream بحيث ينجح هذا.

    String scheme = mUri.getScheme();
    ...
    } else if (ContentResolver.SCHEME_CONTENT.equals(scheme)
            || ContentResolver.SCHEME_FILE.equals(scheme)) {
      try {
        d = Drawable.createFromStream(
                mContext.getContentResolver().openInputStream(mUri),
                null);
    

    -Frameworks/Base/Core/Java/Android/Widget/ImageView.java

لقد عملت. أخذت تلميحًا من MediaStore و MediaProvider. يتم تسمية الملفات التي تحتوي على البيانات بناءً على مزود المحتوى (الدليل) واسم العمود ومعرف الصف ونوع الوسائط. يحصل محتوى المحتوى بعد ذلك على واصف الملف مثل ...

Uri iconUri = Uri.withAppendedPath(Table.getUri(cursor), Table.ICON);
ib.setImageURI(iconUri);

... ومزود المحتوى يستجيب بالنوع ...

@Override
public ParcelFileDescriptor openFile (Uri uri, String mode) {
int imode = 0;
if (mode.contains("w")) imode |= ParcelFileDescriptor.MODE_WRITE_ONLY;
if (mode.contains("r")) imode |= ParcelFileDescriptor.MODE_READ_ONLY;
if (mode.contains("+")) imode |= ParcelFileDescriptor.MODE_APPEND;
List<String> pseg = uri.getPathSegments();
if (pseg.size() < 3) return null;

try {
    File filePath = filePathFromRecord(pseg.get(2), pseg.get(1));
    return ParcelFileDescriptor.open(filePath, imode);
} catch (FileNotFoundException e) {
    e.printStackTrace();
}
return null;
}
هل كانت مفيدة؟

المحلول

الحل الذي يعطيه في النصف السفلي من السؤال صحيح بشكل أساسي. سأحاول إضافة المزيد من التفاصيل هنا.

عندما تفعل getContentResolver().openInputStream(...), ، سيذهب حل المحتوى إلى مزود المحتوى الخاص بك ويتصل به openFile طريقة. هكذا openFile ينظر في contentProvider.java:

public ParcelFileDescriptor openFile(Uri uri, String mode)
     throws FileNotFoundException {
 throw new FileNotFoundException("No files supported by provider at "
         + uri);
}

لذلك هذا يفسر أين يأتي خطأ "لا توجد ملفات مدعومة ..." بالضبط! تتغلب على هذا عن طريق تجاوز openFile الطريقة في فئتك الفرعية وتوفير تطبيقك الخاص. إنه أنيق: تحصل على تحكم مثالي في المكان الذي يتم فيه وضع ملفاتك عندما يقوم أي عميل openInputStream أو openOutputStream.

تعطي عينة الكود في سؤال فرييد تلميحًا كيف يمكن أن يبدو التنفيذ. إليك نسختي المعدلة قليلاً والتي تنشئ أيضًا أدلة وملفات حسب الحاجة. أنا مبتدئ في هذه الأشياء ، لذلك قد لا يكون هذا هو الطريقة المثلى لفعل الأشياء ، لكنه يعطي فكرة. لسبب واحد ، من المحتمل أن تحقق ما إذا كان التخزين الخارجي متاحًا.

@Override
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
    File root = new File(Environment.getExternalStorageDirectory(), 
            "/Android/data/com.example.myapp/cache");
    root.mkdirs();
    File path = new File(root, uri.getEncodedPath());
    // So, if the uri was content://com.example.myapp/some/data.xml,
    // we'll end up accessing /Android/data/com.example.myapp/cache/some/data.xml

    int imode = 0;
    if (mode.contains("w")) {
            imode |= ParcelFileDescriptor.MODE_WRITE_ONLY;
            if (!path.exists()) {
                try {
                    path.createNewFile();
                } catch (IOException e) {
                    // TODO decide what to do about it, whom to notify...
                    e.printStackTrace();
                }
            }
    }
    if (mode.contains("r")) imode |= ParcelFileDescriptor.MODE_READ_ONLY;
    if (mode.contains("+")) imode |= ParcelFileDescriptor.MODE_APPEND;        

    return ParcelFileDescriptor.open(path, imode);
}

نصائح أخرى

يوفر Android طريقة المساعد OpenFileHelper () التي تجعل تنفيذ طريقة OpenFile () سهلة للغاية. كل ما عليك فعله ، لاستخدام هذه الطريقة ، هو توفير موقع الملف في عمود يسمى "_data".

@Override
public ParcelFileDescriptor openFile(Uri uri, String mode)
      throws FileNotFoundException {
   if (URI_MATCHER.match(uri) != PHOTO_ID) {
      throw new IllegalArgumentException
            ("URI invalid. Use an id-based URI only.");
   }
   return openFileHelper(uri, mode);
}

انقر هنا للحصول على التفاصيل

هناك مشكلة في كتابة الملفات رغم ذلك. كيف يعرف مزود المحتوى ثم اكتملت الكتابة؟

عند إغلاق outputstream تم الحصول عليها عبر أ contentResolver.OpenOutputStream () أتمنى أن يكون المقابل (مخصص) ContentProvider لأداء بعض الإجراءات المخصصة. في الوقت الحاضر أنا أستخدم إنشاء ملف FileObserver على ملف مؤقتولكن يبدو أن الطريقة الخاطئة لإنجازها. كانت فكرتي الأولى للنوع الفرعي ParcelfileDescriptor تنتجها contentProvider.openfile () الطريقة ولكن هذا لا يبدو أنه يعمل بالنسبة لي.

ومع ذلك ، كانت هناك بعض الأخطاء التي أدت إلى استخدام myparcelfiledescriptor.

  • لم أتأكد من أن واصف الملف لن يتم جمع القمامة قبل الأوان.
  • لم أحاول التجاوز أخيرا() (يبدو أن الوثائق تشير إلى أن كونك المكان المناسب للقيام بذلك ولكن أغلق() يبدو أنه الخيار الصحيح بالنسبة لي.

خلاصة القول ، هل هناك أي تفاعل بين كائنات الملف كما هو موضح من قبل ContentResolver وتلك من ContentProvider?

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top