음악, 사진 및 비디오 라이브러리의 파일 및 폴더

기존 음악, 사진, 동영상 폴더를 해당 라이브러리에 추가합니다. 라이브러리에서 폴더를 제거하고, 라이브러리의 폴더 목록을 가져오고, 저장된 사진, 음악 및 비디오를 검색할 수도 있습니다.

라이브러리는 기본적으로 알려진 폴더와 사용자가 앱 또는 기본 제공 앱 중 하나를 사용하여 라이브러리에 추가한 다른 폴더를 포함하는 폴더의 가상 컬렉션입니다. 예를 들어, 그림 라이브러리에는 기본적으로 알려진 그림 폴더가 포함되어 있습니다. 사용자는 앱이나 기본 제공된 사진 앱을 사용하여 사진 라이브러리에 폴더를 추가하거나 제거할 수 있습니다.

전제 조건

  • UWP(유니버설 Windows 플랫폼) 앱에 대한 비동기 프로그래밍 이해

    C# 또는 Visual Basic에서 비동기 앱을 작성하는 방법에 대한 자세한 내용은 C# 또는 Visual Basic에서 비동기식 API 호출을 참조하세요. C++에서 비동기 앱을 작성하는 방법은 C++의 비동기 프로그래밍을 참조하세요.

  • 위치에 대한 액세스 권한

    Visual Studio의 매니페스트 디자이너에서 앱 매니페스트 파일을 엽니다. 기능 페이지에서 앱이 관리하는 라이브러리를 선택합니다.

    • 음악 라이브러리
    • 사진 라이브러리
    • 비디오 라이브러리

    자세한 내용은 파일 액세스 권한을 참조하세요.

라이브러리에 대한 참조 가져오기

참고 항목

적절한 기능을 선언하는 것을 잊지 마세요. 자세한 내용은 앱 기능 선언을 참조하세요.  

사용자의 음악, 사진 또는 동영상 라이브러리에 대한 참조를 가져오려면 StorageLibrary.GetLibraryAsync 메서드를 호출합니다. KnownLibraryId 열거형에서 해당 값을 제공합니다.

var myPictures = await Windows.Storage.StorageLibrary.GetLibraryAsync(Windows.Storage.KnownLibraryId.Pictures);

라이브러리의 폴더 목록 가져오기

라이브러리의 폴더 목록을 가져오려면 StorageLibrary.Folders 속성 값을 가져옵니다.

using Windows.Foundation.Collections;
IObservableVector<Windows.Storage.StorageFolder> myPictureFolders = myPictures.Folders;

기본적으로 새 파일이 저장되는 라이브러리에서 폴더 가져오기

새 파일이 기본적으로 저장되는 라이브러리의 폴더를 가져오려면 StorageLibrary.SaveFolder 속성 값을 가져옵니다.

Windows.Storage.StorageFolder savePicturesFolder = myPictures.SaveFolder;

라이브러리에 기존 폴더 추가

라이브러리에 폴더를 추가하려면 StorageLibrary.RequestAddFolderAsync를 호출합니다. 예를 들어, 사진 라이브러리를 사용하면 이 메서드를 호출하면 사진에 이 폴더 추가 단추와 함께 폴더 선택기가 사용자에게 표시됩니다. 사용자가 폴더를 선택하면 폴더는 디스크의 원래 위치에 남아 있으며 StorageLibrary.Folders 속성(및 기본 제공된 사진 앱)의 항목이 됩니다. 하지만 해당 폴더는 파일 탐색기에서 사진 폴더의 자식 항목으로 표시되지 않습니다.

Windows.Storage.StorageFolder newFolder = await myPictures.RequestAddFolderAsync();

라이브러리에서 폴더 제거

라이브러리에서 폴더를 제거하려면 StorageLibrary.RequestRemoveFolderAsync 메서드를 호출하고 제거할 폴더를 지정합니다. 사용자가 제거할 폴더를 선택하도록 하려면 StorageLibrary.FoldersListView 컨트롤(또는 이와 유사한 것)을 사용할 수 있습니다.

StorageLibrary.RequestRemoveFolderAsync를 호출하면 사용자에게 "폴더가 더 이상 표시되지 않지만 삭제되지는 않습니다."라는 확인 대화 상자가 표시됩니다. 즉, 폴더가 디스크의 원래 위치에 유지되고, StorageLibrary.Folders 속성에서 제거되고, 기본 제공 사진 앱에 더 이상 포함되지 않습니다.

다음 예에서는 사용자가 lvPictureFolders라는 ListView 컨트롤에서 제거할 폴더를 선택했다고 가정합니다.

bool result = await myPictures.RequestRemoveFolderAsync(folder);

라이브러리의 폴더 목록 변경에 대한 알림 가져오기

라이브러리의 폴더 목록 변경에 대한 알림을 가져오려면 라이브러리의 StorageLibrary.DefinitionChanged 이벤트에 대한 처리기를 등록합니다.

myPictures.DefinitionChanged += MyPictures_DefinitionChanged;

void HandleDefinitionChanged(Windows.Storage.StorageLibrary sender, object args)
{
    // ...
}

미디어 라이브러리 폴더

디바이스는 사용자와 앱이 미디어 파일을 저장할 수 있도록 미리 정의된 5개의 위치를 제공합니다. 기본 제공된 앱은 사용자가 만든 미디어와 다운로드한 미디어를 모두 이러한 위치에 저장합니다.

다음 도시에 사무실을 새로 설립하려고 합니다.

  • 그림 폴더. 사진이 포함되어 있습니다.

    • 카메라 롤 폴더. 기본 제공 카메라의 사진과 동영상이 포함되어 있습니다.

    • 저장된 사진 폴더. 사용자가 다른 앱에서 저장한 사진이 포함되어 있습니다.

  • 음악 폴더. 노래, 팟캐스트, 오디오북이 포함되어 있습니다.

  • 동영상 폴더. 동영상이 포함되어 있습니다.

사용자 또는 앱은 SD 카드의 미디어 라이브러리 폴더 외부에 미디어 파일을 저장할 수도 있습니다. SD 카드에서 미디어 파일을 안정적으로 찾으려면 SD 카드의 콘텐츠를 검사하거나 사용자에게 파일 선택기를 사용하여 파일을 찾도록 요청합니다. 자세한 내용은 SD 카드에 액세스를 참조하세요.

미디어 라이브러리 쿼리

파일 컬렉션을 가져오려면 원하는 라이브러리와 파일 형식을 지정합니다.

using Windows.Storage;
using Windows.Storage.Search;

private async void getSongs()
{
    QueryOptions queryOption = new QueryOptions
        (CommonFileQuery.OrderByTitle, new string[] { ".mp3", ".mp4", ".wma" });

    queryOption.FolderDepth = FolderDepth.Deep;

    Queue<IStorageFolder> folders = new Queue<IStorageFolder>();

    var files = await KnownFolders.MusicLibrary.CreateFileQueryWithOptions
      (queryOption).GetFilesAsync();

    foreach (var file in files)
    {
        // do something with the music files
    }
}

쿼리 결과에는 내부 스토리지와 이동식 스토리지가 모두 포함됩니다.

사용자는 기본적으로 옵션인 SD 카드에 파일을 저장하도록 선택할 수 있습니다. 그러나 앱은 파일이 SD 카드에 저장되는 것을 허용하지 않을 수 있습니다. 결과적으로 미디어 라이브러리는 디바이스의 내부 스토리지와 SD 카드에 걸쳐 분할될 수 있습니다.

이 가능성을 처리하기 위해 추가 코드를 작성할 필요는 없습니다. 알려진 폴더를 쿼리하는 Windows.Storage 네임스페이스의 메서드는 두 위치의 쿼리 결과를 투명하게 결합합니다. 이러한 결합된 결과를 가져오기 위해 앱 매니페스트 파일에 removableStorage 기능을 지정할 필요도 없습니다.

다음 이미지에 표시된 디바이스의 스토리지 상태를 고려합니다.

images on the phone and sd card

await KnownFolders.PicturesLibrary.GetFilesAsync()를 호출하여 그림 라이브러리의 콘텐츠를 쿼리하면 결과에는 InternalPic.jpg와 SDPic.jpg가 모두 포함됩니다.

사진 작업

카메라가 모든 사진의 저해상도 이미지와 고해상도 이미지를 모두 저장하는 디바이스에서 심층 쿼리는 저해상도 이미지만 반환합니다.

카메라 롤과 저장된 사진 폴더는 심층 쿼리를 지원하지 않습니다.

사진을 캡처한 앱으로 사진 열기

사용자가 나중에 사진을 캡처한 앱에서 사진을 다시 열 수 있도록 하려면 다음 예와 유사한 코드를 사용하여 사진의 메타데이터와 함께 CreatorAppId를 저장할 수 있습니다. 이 예에서 testPhotoStorageFile입니다.

IDictionary<string, object> propertiesToSave = new Dictionary<string, object>();

propertiesToSave.Add("System.CreatorOpenWithUIOptions", 1);
propertiesToSave.Add("System.CreatorAppId", appId);

testPhoto.Properties.SavePropertiesAsync(propertiesToSave).AsyncWait();   

스트림 메서드를 사용하여 미디어 라이브러리에 파일 추가

KnownFolders.PictureLibrary와 같은 알려진 폴더를 사용하여 미디어 라이브러리에 액세스하고 스트림 메서드를 사용하여 미디어 라이브러리에 파일을 추가하는 경우 코드가 여는 모든 스트림을 닫아야 합니다. 그렇지 않으면 적어도 하나의 스트림에 여전히 파일에 대한 핸들이 있기 때문에 이러한 메서드는 예상대로 미디어 라이브러리에 파일을 추가하지 못합니다.

예를 들어, 다음 코드를 실행하면 파일이 미디어 라이브러리에 추가되지 않습니다. 코드 줄 using (var destinationStream = (await destinationFile.OpenAsync(FileAccessMode.ReadWrite)).GetOutputStreamAt(0))에서 OpenAsync 메서드와 GetOutputStreamAt 메서드는 모두 스트림을 엽니다. 그러나 GetOutputStreamAt 메서드로 열린 스트림만 using 문의 결과로 삭제됩니다. 다른 스트림은 열린 상태로 유지되어 파일을 저장할 수 없습니다.

StorageFolder testFolder = await StorageFolder.GetFolderFromPathAsync(@"C:\test");
StorageFile sourceFile = await testFolder.GetFileAsync("TestImage.jpg");
StorageFile destinationFile = await KnownFolders.CameraRoll.CreateFileAsync("MyTestImage.jpg");
using (var sourceStream = (await sourceFile.OpenReadAsync()).GetInputStreamAt(0))
{
    using (var destinationStream = (await destinationFile.OpenAsync(FileAccessMode.ReadWrite)).GetOutputStreamAt(0))
    {
        await RandomAccessStream.CopyAndCloseAsync(sourceStream, destinationStream);
    }
}

스트림 메서드를 성공적으로 사용하여 미디어 라이브러리에 파일을 추가하려면 다음 예와 같이 코드가 여는 모든 스트림을 닫아야 합니다.

StorageFolder testFolder = await StorageFolder.GetFolderFromPathAsync(@"C:\test");
StorageFile sourceFile = await testFolder.GetFileAsync("TestImage.jpg");
StorageFile destinationFile = await KnownFolders.CameraRoll.CreateFileAsync("MyTestImage.jpg");

using (var sourceStream = await sourceFile.OpenReadAsync())
{
    using (var sourceInputStream = sourceStream.GetInputStreamAt(0))
    {
        using (var destinationStream = await destinationFile.OpenAsync(FileAccessMode.ReadWrite))
        {
            using (var destinationOutputStream = destinationStream.GetOutputStreamAt(0))
            {
                await RandomAccessStream.CopyAndCloseAsync(sourceInputStream, destinationStream);
            }
        }
    }
}