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 호환성
주요 method 및 property
- FilePicker.platform.pickFiles()
- 기능: 파일 선택기를 열고 사용자가 파일을 선택하도록 합니다.
- 옵션
type
: 선택할 파일 타입을 지정합니다. 예를 들어,FileType.image
로 설정하면 이미지 파일만 선택할 수 있습니다.allowedExtensions
: 사용자가 선택할 수 있는 파일 확장자를 제한합니다.allowMultiple
: 사용자가 여러 파일을 선택할 수 있도록 할지 여부를 결정합니다.
- FilePickerResult
- 기능:
pickFiles()
메서드의 결과로 반환되는 객체로, 사용자가 선택한 파일에 대한 정보를 담고 있습니다. - 속성
files
: 사용자가 선택한 파일들의 리스트입니다. 각 파일은PlatformFile
객체로 표현됩니다.paths
: 선택한 파일들의 경로 리스트입니다. 각 경로는 문자열로 표현됩니다.count
: 선택한 파일의 수를 나타냅니다.
- 기능:
- 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'), ), ], ), ),
결과
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'), ), ], ), ),