如何在 android 内容提供程序中存储大 blob?
Posted
技术标签:
【中文标题】如何在 android 内容提供程序中存储大 blob?【英文标题】:How to store large blobs in an android content provider? 【发布时间】:2011-04-22 10:39:44 【问题描述】:我有一些大文件(图像和视频)需要存储在内容提供商中。 android文档表明...
如果您要公开的字节数据是 太大而不能放在桌子上—— 比如一个大的位图文件—— 向客户端公开数据的字段 实际上应该包含一个内容:URI 细绳。这是给出的领域 客户端访问数据文件。这 记录还应该有另一个字段, 命名为“_data”,列出了确切的 该文件在设备上的文件路径。 此字段不打算阅读 由客户,但由 内容解析器。客户会打电话 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));
观察/问题...
-
如何正确重建存储/恢复的 uri? (就是表格中的文字)
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;
【问题讨论】:
糟糕!我通过编辑原始文本而不是发布答案来回答我自己的问题,即“如何在和 android 内容提供程序中存储大型 blob”。无论如何,简短的回答是您按照描述的方式使用 openFile 和朋友。 【参考方案1】:问题下半部分phreed给出的解决方案基本正确。我将尝试在此处添加更多详细信息。
当您执行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
时,您可以完美控制文件的放置位置。
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);
【讨论】:
你好@Peteris Caune。我已经像你一样实现了 openFile 方法,但我仍然不知道如何使用 openFileMethod() 中的 ParcelFileDescriptor 对象编写我的文件(*.png),以及现在如何填充"_data"
字段。我花了很多时间寻找一个例子或解释。谢谢
我自己解决了,看来_data
字段是提供者专用的,所以我一直在尝试像游标中的常规字段一样获取数据。我已经注意到我需要改用mResolver.openInputStream(resourceUri);
方法。顺便说一句谢谢。【参考方案2】:
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
【讨论】:
【参考方案3】:但是写入文件有问题。内容提供者如何知道写入是否完成?
当关闭通过 ContentResolver.openOutputStream() 获得的 OutputStream 时,我希望相应的(自定义)ContentProvider 执行一些自定义操作。 目前我正在使用在temporary file 上创建FileObserver 但这似乎是完成它的错误方法。 我的第一个想法是对ContentProvider.openFile() 方法生成的ParcelFileDescriptor 进行子类型化,但这似乎对我不起作用。
话虽如此,有一些错误导致了 MyParcelFileDescriptor 的使用。
我没有确保文件描述符不会过早地被垃圾回收。 我没有尝试覆盖 finally()(文档似乎表明可以这样做,但 close() 似乎是正确的选择给我。底线,ContentResolver 和ContentProvider 看到的文件对象之间是否有任何交互?
【讨论】:
写入文件有问题。内容提供者如何知道写入是否完成? ***.com/questions/4147763/… docs 说:...返回的 ParcelFileDescriptor 归调用者所有,因此他们有责任在完成后关闭它。也就是说,这个方法的实现应该为每个调用创建一个新的 ParcelFileDescriptor。 所以你不必担心关闭ParcelFileDescriptor
实例。以上是关于如何在 android 内容提供程序中存储大 blob?的主要内容,如果未能解决你的问题,请参考以下文章