android - 如何在 android 内容提供程序中存储大 blob?

标签 android inputstream android-contentprovider android-contentresolver

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

If you are exposing byte data that's too big to put in the table itself — such as a large bitmap file — the field that exposes the data to clients should actually contain a content: URI string. This is the field that gives clients access to the data file. The record should also have another field, named "_data" that lists the exact file path on the device for that file. This field is not intended to be read by the client, but by the ContentResolver. The client will call ContentResolver.openInputStream() on the user-facing field holding the URI for the item. The ContentResolver will request the "_data" field for that record, and because it has higher permissions than a client, it should be able to access that file directly and return a read wrapper for the file to the client. -- 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;
}

最佳答案

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

当您执行 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);
}

关于android - 如何在 android 内容提供程序中存储大 blob?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3883211/

相关文章:

Android - Google Analytics V4 异常

android - 禁用 android 的 chrome 下拉刷新功能

Java - UDP 通过套接字发送数据.. 不推荐。所有数据

java - 使用 Android 日历提供程序创建全天事件

c# - MonoDroid SurfaceView

Android: imageview touch 并发布

java - 使用 Java Runtime.getRuntime().exec 调用 aspell 程序的字符集问题

java - 从网络(Twitter)读取 JSON 数据

android从服务注册到内容观察者

android - ContentProvider 实例化失败 - NullPointerException