(Flutter-기초 강의) 13. 파일 관리하기 (로딩 및 표시)

Flutter 개발에 있어 파일 관리와 로딩은 앱의 자원을 관리하기에 필수적인 부분입니다.

특히 이미지와 같은 미디어 파일을 처리할 때는 사용자에게 로딩 중임을 알리는 것이 중요합니다.

다음은 로컬 및 네트워크 경로에서 파일을 로딩하고 표시하는 방법을 예제와 함께 소개하겠습니다.

flutter 파일 관리

File

Flutter에서 파일을 다루는 작업은 dart:io 라이브러리의 File 클래스를 사용하여 수행됩니다.

이 클래스는 파일 시스템에서 파일을 읽고 쓰는 데 필요한 메서드와 속성을 제공하여, Flutter 앱이 로컬 파일과 상호작용할 수 있게 해줍니다.

파일 읽기 및 쓰기

File 의 다음 method들을 사용하여 텍스트나 바이너리 데이터 형태로 파일을 읽고 쓸 수 있습니다.

readAsString()

파일의 내용을 문자열로 읽어들입니다.

String contents = await file.readAsString();
print(contents);
readAsBytes()

파일의 내용을 바이트 리스트로 읽어들입니다.

List<int> bytes = await file.readAsBytes();
print(bytes);
writeAsString()

주어진 문자열을 파일에 씁니다.

다음과 같은 옵션을 넣을 수 있습니다.

await file.writeAsString('Hello, Flutter!');
writeAsBytes()

바이트 리스트를 파일에 씁니다.

List<int> bytes = [72, 101, 108, 108, 111];  // ASCII Code: 'Hello'
await file.writeAsBytes(bytes);
Write 시, option
  • mode: FileMode.append: 이 옵션은 파일의 기존 내용 끝에 새로운 내용을 추가하도록 지정합니다. FileMode.write를 사용하면 기존 내용을 덮어쓰게 됩니다.
  • flush: true: 이 옵션은 쓰기 작업을 수행한 직후에 파일의 변경 내용을 디스크에 즉시 반영하도록 합니다.
    이것은 데이터 손실을 방지하기 위해 중요한 작업 후에 사용될 수 있습니다.
void writeFile() async {
  File file = File('path/to/your/file.dat');

  List<int> bytesToAdd = [72, 101, 108, 108, 111];  // ASCII Code: 'Hello'

  // 기존 파일 끝에 바이트 리스트를 추가합니다.
  await file.writeAsBytes(bytesToAdd, mode: FileMode.append, flush: true);
}
파일 정보 얻기

파일의 현재 상태, 속성 및 메타데이터를 얻기 위한 메서드를 제공합니다.

lastModified()

파일이 마지막으로 수정된 날짜와 시간을 반환합니다.

length()

파일의 크기를 반환합니다. (단위: 바이트)

path

이 속성은 파일의 전체 경로를 반환합니다.

uri

이 속성은 파일의 URI를 반환합니다.

파일 생성 및 삭제:

다음 method를 활용하여 파일을 생성하거나 삭제할 수 있습니다.

create()

파일을 생성합니다.

delete()

파일을 삭제합니다.

Local File system 에서 (로컬) 파일 로딩하기

Flutter 앱에서 사용자의 파일 시스템에 접근하여 파일을 선택하고 로딩하기 위해서는 외부 패키지를 사용하는 것이 일반적입니다.

본 포스팅에서는 file_picker 패키지를 이용해서 파일 시스템에서 파일을 불러오겠습니다.

file_picker

사용자가 자신의 디바이스에서 파일을 선택할 수 있도록 하는 UI를 제공하는 패키지입니다.

사용자는 이를 통해 파일 시스템을 탐색하고, 원하는 파일을 앱으로 가져올 수 있습니다.

https://pub.dev/packages/file_picker

Platform 호환성
file_picker compatibility chart
from https://pub.dev/packages/file_picker
주요 method 및 property
  1. FilePicker.platform.pickFiles()
    • 기능: 파일 선택기를 열고 사용자가 파일을 선택하도록 합니다.
    • 옵션
      • type: 선택할 파일 타입을 지정합니다. 예를 들어, FileType.image로 설정하면 이미지 파일만 선택할 수 있습니다.
      • allowedExtensions: 사용자가 선택할 수 있는 파일 확장자를 제한합니다.
      • allowMultiple: 사용자가 여러 파일을 선택할 수 있도록 할지 여부를 결정합니다.
  2. FilePickerResult
    • 기능: pickFiles() 메서드의 결과로 반환되는 객체로, 사용자가 선택한 파일에 대한 정보를 담고 있습니다.
    • 속성
      • files: 사용자가 선택한 파일들의 리스트입니다. 각 파일은 PlatformFile 객체로 표현됩니다.
      • paths: 선택한 파일들의 경로 리스트입니다. 각 경로는 문자열로 표현됩니다.
      • count: 선택한 파일의 수를 나타냅니다.
  3. PlatformFile
    • 기능: 사용자가 선택한 각 파일에 대한 정보를 포함합니다.
    • 속성
      • name: 파일의 이름입니다.
      • size: 파일의 크기입니다 (단위: byte).
      • path: 파일의 전체 경로입니다. 이 경로를 사용하여 파일에 접근할 수 있습니다.
      • bytes: 파일의 바이트 데이터입니다. 주로 메모리에서 바로 파일 데이터를 사용하고 싶을 때 활용됩니다.

예제

전체 코드는 My Git Repository_3_communication_basic 코드를 참조해주세요.

1. 파일을 선택하여 로드 합니다. (이미지 파일만 로드하도록 옵션을 부여합니다.)

  Future<void> pickImage() async {
    FilePickerResult? result = await FilePicker.platform.pickFiles(
      type: FileType.image, // select only images
    );

    if (result != null) {
      setState(() {
        isImageLoading = true;

        imgFile =
            File(result.files.single.path!); // Make the selected image file
        print('File Path: ${imgFile!.path}');
      });
    } else {
      // If the user cancels the selection
      print("No image selected");
    }

    setState(() {
      isImageLoading = false;
    });
  }

2. 버튼을 클릭하여 파일을 선택하여 로드 하고, 이미지 파일(imgFile)이 로드되면 Image 클래스를 사용하여 영상을 표시합니다.

      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            if (imgFile != null)
              Image.file(imgFile!) // Display the image file on the screen
            else
              isImageLoading
                  ? const CircularProgressIndicator()
                  : const Text('You have not yet picked an image'),
            const SizedBox(height: 20),
            ElevatedButton(
              onPressed: () {
                pickImage(); // Open the file selector and select an image by file_picker
              },
              child: const Text('Select Image with file_picker'),
            ),
          ],
        ),
      ),

결과

local file manager result

Network 파일 표시하기

앞선 포스팅(Flutter-기초 강의) 12. http 통신하기 (feat. http, dio 패키지)에서 학습한 dio 패키지를 이용하여 파일을 다운로드 하여 저장한 후, 저장된 파일을 표시하도록 하는 방법을 알아보겠습니다.

다운 받은 파일을 파일 시스템에 저장하기 위하여 path_provider를 함께 사용하겠습니다.

path_provider

앱 데이터를 저장할 때 사용하는 특정 위치에 대한 경로를 찾는 데 도움을 주는 패키지입니다.

이는 앱 내에서 생성되거나 관리되는 파일을 저장하고 검색할 때 사용됩니다.

https://pub.dev/packages/path_provider

주요 method 및 property
  • getTemporaryDirectory(): 임시 디렉토리의 경로를 제공합니다.
  • getApplicationDocumentsDirectory(): 앱 문서 디렉토리의 경로를 제공합니다.
  • getExternalStorageDirectory(): 외부 저장소 디렉토리의 경로를 제공합니다.
  • getApplicationSupportDirectory(): 애플리케이션 지원 디렉토리의 경로를 제공합니다.

예제

전체 코드는 My Git Repository_3_communication_basic 코드를 참조해주세요.

1. Network상에 존재하는 이미지 경로를 통해 다운받아 임시폴더에 저장합니다.
(isImageLoading 을 통해 다운로드 상태를 나타냅니다.)

  // function to download and save image
  Future<void> downloadAndSaveImage(String url, String fileName) async {
    try {
      if (downloadedImage != null) {
        downloadedImage!.delete();
        downloadedImage = null;
      }

      // get temporary directory of device
      final dir = await getTemporaryDirectory();

      // download image from given url
      Dio dio = Dio();

      setState(() {
        isImageLoading = true;
      });

      Response response = await dio.get(
        url,
        options: Options(responseType: ResponseType.bytes),
      );
      final File file = File('${dir.path}/$fileName');
      file.writeAsBytesSync(response.data);

      setState(() {
        isImageLoading = false;
        downloadedImage = file;
      });
    } catch (e) {
      setState(() {
        isImageLoading = false;
      });
      print(e.toString());
    }
  }

2. 버튼을 누르면 특정 경로 이미지 파일을 다운로드 합니다.
(다운로드 중에는 CircularProgressIndicator()를 통해 다운로드 상태를 표시합니다.)

      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            if (downloadedImage != null)
              Image.file(
                  downloadedImage!) // Display the image file on the screen
            else
              isImageLoading
                  ? const CircularProgressIndicator()
                  : const Text('You have not yet picked an image'),
            const SizedBox(height: 20),
            ElevatedButton(
              onPressed: () {
                downloadAndSaveImage(
                    'https://firebasestorage.googleapis.com/v0/b/denv-funcs.appspot.com/o/devitworld_logo.png?alt=media&token=8642c35a-7664-4af7-9dfd-b166e2bdb6a6',
                    'devitworld.png');
              },
              child: const Text('Download Image'),
            ),
          ],
        ),
      ),

결과

network file manager result

참고링크

Leave a Comment