[Flutter] multipart/form-data를 통해 서버로 파일 업로드하기


이슈

HTTP 통신에서 단순 텍스트가 아닌 바이너리 형식의 파일을 보내고자 할 때 content-type으로 multipart/form-data 형식을 사용한다. 그렇다면 flutter에서는 어떻게 파일을 선택하고 이를 서버로 전송할 수 있을까?


해결

라이브러리

코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import 'package:dio/dio.dart';
import 'package:file_picker/file_picker.dart';

// 1개의 파일 업로드
Future<void> uploadFile() async {
    // file picker를 통해 파일 선택
    FilePickerResult? result = await FilePicker.platform.pickFiles();

    if (result != null) {
        final filePath = result.files.single.path;
        
        // 파일 경로를 통해 formData 생성
        var dio = Dio();
        var formData = FormData.fromMap({
            'file' : await MultipartFile.fromFile(filePath!)
        });

        // 업로드 요청
        final response = await dio.post('/upload', data: formData);

    } else {
        // 아무런 파일도 선택되지 않음.
    }
}


// 여러개의 파일 업로드
Future<void> uploadFiles() async {
    // file picker를 통해 파일 여러개 선택
    FilePickerResult? result = await FilePicker.platform.pickFiles(allowMultiple: true);

    if (result != null) {
        final filePaths = result.paths;

        // 파일 경로를 통해 formData 생성
        var dio = Dio();
        var formData = FormData.fromMap({
            'files' : List.generate(filePaths,
          (index) => MultipartFile.fromFileSync(filePaths[index]!))
        });

        // 업로드 요청
        final response = await dio.post('/upload', data: formData);

    } else {
        // 아무런 파일도 선택되지 않음.
    }
}


고찰

file_picker

우선 file_picker의 경우 대체제가 없을 정도로 널리 알려진 파일 선택 라이브러리이다. 공식 문서를 보면 알 수 있겠지만 다양한 기능을 지원함과 동시에, flutter라는 이름에 걸맞게 Android/iOS 뿐만 아니라 Windows, MacOS, Linux와 같이 데스크탑 OS에서도 모두 잘 동작한다.

http vs dio

하지만 HTTP 통신 라이브러리와 같은 경우에는 http라는 flutter 공식 라이브러리가 있음에도 dio를 선택한 것에는 의문이 들 수 있다. 필자도 순정 라이브러리를 선호하는 만큼 단순히 diohttp보다 개발자 입장에서 조금 더 편하게 설계되어 있을 뿐, 필수는 아니라고 생각되어 http를 사용했었다. 하지만 파일 업로드를 시도하게 되면서 dio의 강력함을 깨닫고 http에서 dio로 완전히 넘어오게 되었다. diohttp에 비해 갖는 가장 큰 장점은 다음과 같다.

dio는 request 및 response 에 사용되는 data를 자동으로 파싱해준다!

즉, 데이터를 주고 받을 때 http 라이브러리는 jsondecode하여 dartMap<String,dynamic> 자료구조에 매핑을 하는 등의 파싱이 필요하지만 dio를 사용하면 따로 데이터의 파싱 없이 알아서 내가 원하는 데이터 타입으로 변환해준다. 그리고 이와 같은 성질은 위와 같이 파일 업로드에 사용되는 multipart/form-data 형식의 통신을 할 때 진가를 발휘한다. 코드를 보면 알겠지만 form-data를 단순히 dartMap 자료구조에 key-value 값을 채워넣는 것으로 생성할 수 있다. 간단한 json 타입 데이터의 통신을 할 때에도 일일이 파싱이 필요한 http 라이브러리로 이 작업을 한다면 훨씬 귀찮아진다.

위와 같은 특징 때문에 httpdio를 같이 사용하면 데이터의 파싱 때문에 개발자 입장에서 타입에 굉장히 헷갈릴 수 있다. 따라서 둘 중 하나를 콕 정해서 사용하는 것이 좋고 서버와의 통신이 잦은 서비스라면 dio를 사용하는 것을 추천한다.

다음 포스트에서는 여러 타입의 파일 중 이미지를 업로드 할 때 이미지를 자르거나 압축을 통해 용량을 줄이는 방법에 대해 작성하도록 하겠다.

0%