13

我有一些需要存储在内容提供商中的大文件(图像和视频)。android文档表明...

如果您要公开太大而无法放入表本身的字节数据(例如大型位图文件),则将数据公开给客户端的字段实际上应该包含一个内容:URI 字符串。这是允许客户端访问数据文件的字段。该记录还应该有另一个名为“_data”的字段,其中列出了该文件在设备上的确切文件路径。此字段不打算由客户端读取,而是由 ContentResolver 读取。客户端将在包含项目 URI 的面向用户的字段上调用 ​​ContentResolver.openInputStream()。ContentResolver 将为该记录请求“_data”字段,并且由于它具有比客户端更高的权限,它应该能够直接访问该文件并将该文件的读取包装返回给客户端。-- http://developer.android.com/guide/topics/providers/content-providers.html#creating

我很难找到一个例子。特别是我希望在 ImageView 上下文中使用位图。考虑以下代码准代码(它不起作用)...

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 得到了提示。包含数据的文件根据内容提供者(目录)、列名、行 ID 和媒体类型命名。内容解析器然后像这样获取文件描述符......

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;
}
4

3 回答 3

12

问题下半部分给出的解决方案 phreed 基本上是正确的。我将尝试在此处添加更多详细信息。

当您这样做时getContentResolver().openInputStream(...),内容解析器将转到您的内容提供者并调用其openFile方法。这是ContentProvider.javaopenFile中的外观:

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

因此,这解释了“不支持文件...”错误的确切来源!您可以通过覆盖openFile子类中的方法并提供自己的实现来解决此问题。它很简洁:当任何客户端执行openInputStreamopenOutputStream.

phreed 问题中的代码示例提示了实现的外观。这是我稍作修改的版本,它还根据需要创建目录和文件。我是这方面的新手,所以这可能不是最佳的做事方式,但它提供了一个想法。一方面,它可能应该检查外部存储是否可用。

@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);
}
于 2010-12-02T14:25:30.907 回答
1

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);
}

点击这里查看详细信息

于 2013-02-17T10:05:52.493 回答
0

但是写入文件有问题。内容提供者如何知道写入是否完成?

当关闭通过ContentResolver.openOutputStream()获得的 OutputStream 时,我希望相应的(自定义)ContentProvider执行一些自定义操作。目前我正在使用 在临时文件上创建FileObserver , 但这似乎是完成它的错误方法。我的第一个想法是对由ContentProvider.openFile()方法生成的ParcelFileDescriptor进行子类型化,但这似乎对我不起作用。

话虽如此,有一些错误导致了 MyParcelFileDescriptor 的使用。

  • 我没有确保文件描述符不会过早地被垃圾收集。
  • 我没有尝试覆盖finally()(文档似乎表明是这样做的地方,但close()对我来说似乎是正确的选择。

最重要的是, ContentResolver看到的文件对象和ContentProvider的文件对象之间是否有任何交互?

于 2010-11-13T20:19:29.237 回答