2016년 10월 26일 수요일

[안드로이드] MediaStore에서 파일 다루기

MediaStore

  • 안드로이드 시스템에서 제공하는 미디어 데이터 DB
  • 시스템은 파일시스템에 저장되어 있는 미디어 파일들을 이 DB에 추가하여, 여러 앱에서 이용할 수 있도록 한다.
  • 시스템이 제공하는 Provider를 이용해 미디어 파일(오디오, 이미지, 비디오)를 쿼리할 수 있다.
  • API 11부터는 미디어 파일 이외에도, 일반 파일을 쿼리할 수 있는 API가 추가되었다.

MediaStore.Files

  • 파일을 다루기위해 추가된 API
  • getContentUri() 메서드를 호출하여 쿼리할 Uri를 얻을 수 있다.
    • 인자로 internalexternal을 넘겨야 하며, 이 값으로 스토리지 영역을 구분한다.

MediaStore.Files.FileColumns

예제 소스

  • 다음 예제는 확장자가 txt인 파일을 쿼리하는 예제이다.
public static void queryFiles(Context context) {
    // External 스토리지의 URI 획득
    final Uri uri = MediaStore.Files.getContentUri("external");
    //ID, 파일명, mimeType, 파일크기 을 가져오도록 설정
    final String[] projection = new String[] {MediaStore.Files.FileColumns._ID, MediaStore.Files.FileColumns.DISPLAY_NAME, MediaStore.Files.FileColumns.MIME_TYPE, MediaStore.Files.FileColumns.SIZE};
    //확장자가 txt인 mimeType을 쿼리
    final String selection = MediaStore.Files.FileColumns.MIME_TYPE + "=?";
    final String[] selectionArgs = new String[] {MimeTypeMap.getSingleton().getMimeTypeFromExtension("txt")};

    // 쿼리 수행 후, 컬럼명, 값 출력
    Cursor cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null);
    while (cursor != null && cursor.moveToNext()) {
        int columnCount = cursor.getColumnCount();
        for (int i = 0 ; i < columnCount ; i++ ) {
            Logger.logDebug(cursor.getColumnName(i) + " : " + cursor.getString(i));
        }
        Logger.logDebug("----------------------------");
    }
}
  • 다음은 audio, image, video를 제외한 형식의 파일 목록을 얻는 예이다
public static void queryFiles(Context context) {
    // External 스토리지의 URI 획득
    final Uri uri = MediaStore.Files.getContentUri("external");
    //ID, 파일명, mimeType, 파일크기 을 가져오도록 설정
    final String[] projection = new String[] {MediaStore.Files.FileColumns._ID, MediaStore.Files.FileColumns.DISPLAY_NAME, MediaStore.Files.FileColumns.MIME_TYPE, MediaStore.Files.FileColumns.SIZE};
    //MEDIA_TYPE_NONE인 데이터 쿼리
    final String selection = MediaStore.Files.FileColumns.MEDIA_TYPE + "=?";
    final String[] selectionArgs = new String[] {MediaStore.Files.FileColumns.MEDIA_TYPE_NONE};

    // 쿼리 수행 후, 컬럼명, 값 출력
    Cursor cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null);
    while (cursor != null && cursor.moveToNext()) {
        int columnCount = cursor.getColumnCount();
        for (int i = 0 ; i < columnCount ; i++ ) {
            Logger.logDebug(cursor.getColumnName(i) + " : " + cursor.getString(i));
        }
        Logger.logDebug("----------------------------");
    }
}
  • 다음은 external storage에서 특정 검색 단어를 쿼리하는 예이다
public static void queryFiles(Context context) {
    // External 스토리지의 URI 획득
    final Uri uri = MediaStore.Files.getContentUri("external");
    //ID, 파일명, mimeType, 파일크기 을 가져오도록 설정
    final String[] projection = new String[] {MediaStore.Files.FileColumns._ID, MediaStore.Files.FileColumns.DISPLAY_NAME, MediaStore.Files.FileColumns.MIME_TYPE, MediaStore.Files.FileColumns.SIZE};
    //파일명에 Keyword 가 있으면 쿼리
    String keyword = "2016"
    final String selection = MediaStore.Files.FileColumns.TITLE + " LIKE ?";
    final String[] selectionArgs = new String[] {"%" + keyword + "%"};

    // 쿼리 수행 후, 컬럼명, 값 출력
    Cursor cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null);
    while (cursor != null && cursor.moveToNext()) {
        int columnCount = cursor.getColumnCount();
        for (int i = 0 ; i < columnCount ; i++ ) {
            Logger.logDebug(cursor.getColumnName(i) + " : " + cursor.getString(i));
        }
        Logger.logDebug("----------------------------");
    }
}
  • 다음은 미디어 DB에 들어있는 하나의 Row를 전부 출력한 내용이다.
  • 기존 미디어 DB를 그대로 이용하기 때문에, 미디어 데이터에 최적화되어 있다. 그러므로 파일 데이터에는 불필요한 컬럼이 많으니, 적절한 projection을 작성할 필요가 있다.
[MediaFileTest_23]: _id : 15355
[MediaFileTest_23]: _data : /storage/emulated/0/data/colornote/backup/1477006699767-AUTO.doc
[MediaFileTest_23]: _size : 37244
[MediaFileTest_23]: format : 47747
[MediaFileTest_23]: parent : 54
[MediaFileTest_23]: date_added : 1477042269
[MediaFileTest_23]: date_modified : 1477006700
[MediaFileTest_23]: mime_type : application/msword
[MediaFileTest_23]: title : 1477006699767-AUTO
[MediaFileTest_23]: description : null
[MediaFileTest_23]: _display_name : 1477006699767-AUTO.doc
[MediaFileTest_23]: picasa_id : null
[MediaFileTest_23]: orientation : null
[MediaFileTest_23]: latitude : null
[MediaFileTest_23]: longitude : null
[MediaFileTest_23]: datetaken : null
[MediaFileTest_23]: mini_thumb_magic : null
[MediaFileTest_23]: bucket_id : 1750785639
[MediaFileTest_23]: bucket_display_name : backup
[MediaFileTest_23]: isprivate : null
[MediaFileTest_23]: title_key : null
[MediaFileTest_23]: artist_id : null
[MediaFileTest_23]: album_id : null
[MediaFileTest_23]: composer : null
[MediaFileTest_23]: track : null
[MediaFileTest_23]: year : null
[MediaFileTest_23]: is_ringtone : null
[MediaFileTest_23]: is_music : null
[MediaFileTest_23]: is_alarm : null
[MediaFileTest_23]: is_notification : null
[MediaFileTest_23]: is_podcast : null
[MediaFileTest_23]: album_artist : null
[MediaFileTest_23]: duration : null
[MediaFileTest_23]: bookmark : null
[MediaFileTest_23]: artist : null
[MediaFileTest_23]: album : null
[MediaFileTest_23]: resolution : null
[MediaFileTest_23]: tags : null
[MediaFileTest_23]: category : null
[MediaFileTest_23]: language : null
[MediaFileTest_23]: mini_thumb_data : null
[MediaFileTest_23]: name : null
[MediaFileTest_23]: media_type : 0
[MediaFileTest_23]: old_id : null
[MediaFileTest_23]: storage_id : 65537
[MediaFileTest_23]: is_drm : 0
[MediaFileTest_23]: width : null
[MediaFileTest_23]: height : null
[MediaFileTest_23]: is_sound : 0
[MediaFileTest_23]: year_name : <unknown>
[MediaFileTest_23]: genre_name : <unknown>
[MediaFileTest_23]: recently_played : 0
[MediaFileTest_23]: most_played : 0
[MediaFileTest_23]: recently_added_remove_flag : 0
[MediaFileTest_23]: is_favorite : 0
[MediaFileTest_23]: resumePos : 0
[MediaFileTest_23]: isPlayed : 0
[MediaFileTest_23]: face_count : -1
[MediaFileTest_23]: scan_pri : 0
[MediaFileTest_23]: weather_ID : 0
[MediaFileTest_23]: recordingtype : -1
[MediaFileTest_23]: group_id : 0
[MediaFileTest_23]: city_ID : 0
[MediaFileTest_23]: spherical_mosaic : 0
[MediaFileTest_23]: label_id : 0
[MediaFileTest_23]: is_memo : 0
[MediaFileTest_23]: addr : null
[MediaFileTest_23]: langagecode : null
[MediaFileTest_23]: is_secretbox : 0
[MediaFileTest_23]: sampling_rate : 0
[MediaFileTest_23]: bit_depth : 0
[MediaFileTest_23]: is_360_video : 0
[MediaFileTest_23]: pic_rating : -1
[MediaFileTest_23]: sef_file_type : -1
[MediaFileTest_23]: reusable : 0
[MediaFileTest_23]: recorded_number : null
[MediaFileTest_23]: title_bucket : null
[MediaFileTest_23]: title_label : null
[MediaFileTest_23]: recording_mode : 0
[MediaFileTest_23]: is_ringtone_theme : 0
[MediaFileTest_23]: is_notification_theme : 0
[MediaFileTest_23]: is_alarm_theme : 0
[MediaFileTest_23]: type3dvideo : <unknown>
[MediaFileTest_23]: video_view_mode : -1
[MediaFileTest_23]: video_codec_info : <unknown>
[MediaFileTest_23]: audio_codec_info : <unknown>
[MediaFileTest_23]: sef_file_sub_type : -1
[MediaFileTest_23]: smartcrop_rect : <unknown>

댓글 7개:

  1. 잘 보겠습니다.
    감사합니다.

    답글삭제
  2. 작성자가 댓글을 삭제했습니다.

    답글삭제
  3. projection 다 정리해두신 거에서 감탄했습니다! 개발하는 데에 참고하겠습니다. 감사합니다.

    답글삭제
  4. 오래된 글에 댓글 달아 죄송한데....
    혹시 이렇게 getContentResolver()로 쿼리해서 구한 URI이 있다면
    이 URI가 가리키는 파일을 삭제하는게 가능할까요? 물론 R에서 말입니다

    답글삭제
    답글
    1. R에서 Scoped Storage를 사용하고 앱에 속한 파일이 아니라면,
      uri를 이용하여 파일을 삭제하는건 막힌 것으로 알고 있습니다.

      https://developer.android.com/training/data-storage/shared/media#remove-item

      위의 공식문서에서 MediaStore.createDeleteRequest()를 사용하라고 되어있는데, 이부분 확인해보시면 좋을 것 같아요.

      삭제
  5. 작성자가 댓글을 삭제했습니다.

    답글삭제