美文网首页Android Tips涛锅锅的Android资料Android开发
以SQL注入的方式高效查询Android设备上所有图片所在的文件

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

作者: carlos23 | 来源:发表于2017-09-20 22:34 被阅读213次

    图片选择器相信很多人一定不会陌生,现在只要带点图片功能的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"
    );
    

    相关文章

      网友评论

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

        本文链接:https://www.haomeiwen.com/subject/vunnsxtx.html