如何在Android内容提供商中存储大blob?

时间:2010-10-07 15:25:27

标签: android inputstream android-contentprovider android-contentresolver

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

  

如果您正在公开字节数据   太大了,无法放入桌子本身 -   比如一个大的位图文件 -   将数据公开给客户端的字段   实际上应该包含一个内容:URI   串。这是给出的领域   客户端访问数据文件。该   记录还应该有另一个字段,   名为“_data”,列出确切的   设备上该文件的文件路径。   此字段无意阅读   由客户,但由   ContentResolver的。客户会打电话   ContentResolver.openInputStream()on   持有URI的面向用户的字段   对于该项目。 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);
    

    - 框架/碱/核心/ JAVA /机器人/插件/ ImageView.java

  3. 我得到了它的工作。 我从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;
    }
    

3 个答案:

答案 0 :(得分:12)

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

执行getContentResolver().openInputStream(...)后,内容解析程序将转到您的内容提供商并调用其openFile方法。这就是openFileContentProvider.java中的显示方式:

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

答案 1 :(得分: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);
}

Click here for detail

答案 2 :(得分:0)

虽然写文件有问题。内容提供商如何知道写入是否已完成?

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

据说有一些错误导致了MyParcelFileDescriptor的使用。

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

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