Android Tips涛锅锅的Android资料Android开发

以SQL注入的方式高效查询Android设备上所有图片所在的文件

2017-09-20  本文已影响213人  carlos23

图片选择器相信很多人一定不会陌生,现在只要带点图片功能的app都会做一个选择图片的功能,这就很有可能会遇到这样一个需求,就是先展示出所有图片所在的文件夹,再点击进入文件夹后展示该文件夹下的所有图片,在这里如何获取到Android设备上所有图片所在的文件夹便是一个棘手的问题。

MediaStore没有提供查询文件夹的方法,只有一个关于文件夹的属性:MediaStore.Files.FileColumns.PARENT,官方文档的解释是:

The index of the parent directory of the file
Type: INTEGER

虽然并不知道这个index值的实际含义,但至少可以用它来区分不同的文件是不是在同一个文件夹下了。

另外,在使用ContentResolver查询MediaStore的数据时,如果参数写错了,异常信息中会暴露出系统编译的SQL语句。

例如执行以下的查询:

context.getContentResolver().query(
        MediaStore.Files.getContentUri("external"),
        new String[]{"abc", "def"},
        MediaStore.Files.FileColumns.MEDIA_TYPE + " = " + MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE,
        null,
        MediaStore.Files.FileColumns.DATE_MODIFIED + " DESC"
);

会报出这样的崩溃:

android.database.sqlite.SQLiteException: no such column: abc (code 1): , while compiling: SELECT abc, def FROM files WHERE (media_type = 1) ORDER BY date_modified DESC

观察这个SQL语句的结构:

SELECT abc, def FROM files WHERE ( media_type = 1 ) ORDER BY date_modified DESC

我们可以知道调用query()方法时传入的参数的对应位置,以上加粗的字是系统生成的(包括WHERE那里的一对小括号),非加粗部分是我们自己传入的参数。

我们不难通过SQL注入的方式,拼凑复杂的查询语句得出我们想要的东西。

现在想要查询出所有图片所在的文件夹,我们只要对MediaStore.Files.FileColumns.PARENT使用group by关键字即可,具体的SQL语句如下:

SELECT COUNT(parent), _data
FROM files
WHERE (media_type = 1) 
GROUP BY (parent)

其中parent就是MediaStore.Files.FileColumns.PARENT的取值;media_typeMediaStore.Files.FileColumns.MEDIA_TYPE_dataMediaStore.Files.FileColumns.DATA,表示文件的路径,我们可以通过截取文件的路径获取到文件夹的路径。

转换为Java代码便是如下的查询:

Cursor cursor = context.getContentResolver().query(
        MediaStore.Files.getContentUri("external"),
        new String[]{
                "COUNT(" + MediaStore.Files.FileColumns.PARENT + ")",
                MediaStore.Files.FileColumns.DATA,
        },
        MediaStore.Files.FileColumns.MEDIA_TYPE + " = " + MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE
                + " ) GROUP BY (" + MediaStore.Files.FileColumns.PARENT,
        null,
        null
);
if (cursor != null) {
    while (cursor.moveToNext()) {
        int imageFileCountInFolder = cursor.getInt(0);
        String imageFilePath = cursor.getString(1);
        File folderFile = new File(imageFilePath).getParentFile();
        Log.d("test", String.format(Locale.getDefault(), "文件夹路径:%s, 文件夹中的图片个数:%d", folderFile.getAbsolutePath(), imageFileCountInFolder));
    }
    cursor.close();
}

这里顺便把每个文件夹中的图片个数也查询到了。然而,往往我们还要显示文件夹的封面图片,而且还要是文件夹里最新的一张图片。上面的查询得到的图片是任意的,并没有保证是最新的一张图片。

能否在一次查询中解决这个问题?否则如果多次查询数据库导致多次IO操作,或是在Java代码中处理,都耗时、耗内存。答案是可以的,不过语句要复杂一点。

先写好SQL语句:

SELECT COUNT(parent) AS fileCount, _data
FROM (SELECT * FROM files WHERE (media_type = 1) ORDER BY date_modified)
GROUP BY (parent)
ORDER BY fileCount DESC

这里使用子查询是因为最外层的ORDER BY针对的是分组后的数据进行排序。组内排序有多种方式,这里我们就使用子查询。转换为Java代码如下:

Cursor cursor = context.getContentResolver().query(
        MediaStore.Files.getContentUri("external"),
        new String[]{
                "COUNT(" + MediaStore.Files.FileColumns.PARENT + ") AS fileCount",
                MediaStore.Files.FileColumns.DATA + " FROM (SELECT *",
        },
        MediaStore.Files.FileColumns.MEDIA_TYPE + " = " + MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE + ")"
                + " ORDER BY " + MediaStore.Files.FileColumns.DATE_MODIFIED + " )"
                + " GROUP BY (" + MediaStore.Files.FileColumns.PARENT,
        null,
        "fileCount DESC"
);
if (cursor != null) {
    while (cursor.moveToNext()) {
        int imageFileCountInFolder = cursor.getInt(0);
        String latestImageFilePath = cursor.getString(1);
        File folderFile = new File(latestImageFilePath).getParentFile();
        
        Log.d("test", "文件夹路径:" + folderFile.getAbsolutePath());
        Log.d("test", "文件夹中的图片个数:" + imageFileCountInFolder);
        Log.d("test", "文件夹中最新的一张图片的路径:" + latestImageFilePath);
    }
    cursor.close();
}

查询某个文件夹下的所有图片的代码:

String folderPath = "/storage/emulated/0/Pictures";
Cursor cursor = getContext().getContentResolver().query(
        MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
        null,
        MediaStore.Images.ImageColumns.DATA + " like '%" + folderPath + "%'",
        null,
        MediaStore.Images.ImageColumns.DATE_MODIFIED + " DESC"
);
上一篇下一篇

猜你喜欢

热点阅读