如何在 android 内容提供程序中存储大 blob? [英] How to store large blobs in an android content provider?

查看:35
本文介绍了如何在 android 内容提供程序中存储大 blob?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have some large files (images and video) which I need to store in a content provider. The android documentation indicates...

如果您要公开的字节数据是太大而不能放在桌子本身 -比如一个大的位图文件——向客户端公开数据的字段实际上应该包含一个内容:URI细绳.这是给客户端访问数据文件.这记录还应该有另一个字段,名为_data",列出了确切的该文件在设备上的文件路径.此字段不可读取由客户,但由内容解析器.客户会打电话ContentResolver.openInputStream() 上持有 URI 的面向用户的字段为项目.ContentResolver 将为此请求_data"字段记录,因为它有更高的权限比客户端,它应该能够直接访问该文件并返回文件的读取包装器给客户.-- http://developer.android.com/guide/topics/providers/content-providers.html#creating

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 的上下文中使用位图.考虑下面的代码准代码(它不起作用)...

I am having some difficulty finding an example. In particular I wish to use the bitmap in the context an ImageView. Consider the following code quasi-code (it doesn't work)...

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

观察/问题...

  1. 如何正确重建存储/恢复的 uri?(是表格中的文字)
  2. setImageURI 实现利用了内容解析 openInputStream 所以这应该可以工作.

  1. How can the stored/recovered uri be reconstructed correctly? (it is text in the table)
  2. The setImageURI implementation makes use of the content resolve openInputStream so this should work.

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

--frameworks/base/core/java/android/widget/ImageView.java

我成功了.我从 MediaStore 和 MediaProvider 得到了一个提示.包含数据的文件根据内容提供者(目录)、列名、行 ID 和媒体类型命名.然后内容解析器像这样获取文件描述符......

I got it working. I took a hint from the MediaStore and MediaProvider. The files which contain the data are named based on the content provider (directory), the column name, the row id and the media type. The content resolver then acquires the file descriptor like so...

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

...内容提供者以实物回应...

...and the content provider responds in kind...

@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 在问题下半部分给出的解决方案基本正确.我会尝试在此处添加更多详细信息.

The solution phreed gives in the bottom half of question is basically correct. I'll try to add some more details here.

当您执行 getContentResolver().openInputStream(...) 时,内容解析器将转到您的内容提供者并调用其 openFile 方法.这是 openFileContentProvider.java:

When you do getContentResolver().openInputStream(...), content resolver will go to your content provider and call its openFile method. This is how the openFile looks in ContentProvider.java:

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

所以这解释了不支持文件..."错误的确切来源!您可以通过覆盖子类中的 openFile 方法并提供您自己的实现来解决这个问题.它很简洁:当任何客户端执行 openInputStreamopenOutputStream 时,您可以完美地控制文件的放置位置.

So this explains where the "No files supported ..." error exactly comes from! You get around this by overriding openFile method in your subclass and providing your own implementation. It's neat: you get perfect control of where your files get placed when any client does openInputStream or openOutputStream.

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

Code sample in phreed's question gives a hint how the implementation could look like. Here's my slightly modified version which also creates directories and files as needed. I'm novice at this stuff so this might not be the optimal way of doing things, but it gives an idea. For one thing, it should probably check if external storage is available.

@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 内容提供程序中存储大 blob?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆