본문 바로가기
Flutter

[Flutter] webview 이미지 전송하는 방법

by pin9___9 2024. 6. 27.
728x90

Flutter 하이브리드 앱을 개발하면서 이미지 업로드 기능이 필요할 때가 있습니다. iOS는 비교적 쉽게 해결할 수 있지만, Android에서는 약간의 추가 작업이 필요합니다. 이 글에서는 Flutter와 WebView를 이용해 이미지를 선택하고 업로드하는 방법을 정리하겠습니다.

 

iOS 이미지 업로드 방법

iOS에서는 <input type='file'> 태그를 클릭하면 사진 보관함, 사진 찍기, 파일 선택 중 하나를 선택할 수 있는 옵션이 나타납니다. 사진 찍기를 선택할 경우 카메라 접근 허용을 요청하는 팝업이 뜨게 됩니다. 이를 해결하기 위해 info.plist 파일에 아래 코드를 추가해야 합니다.

 

<key>NSPhotoLibraryUsageDescription</key>
<string>사진 보관함 접근 이유를 자세히 표기</string>
<key>NSCameraUsageDescription</key>
<string>카메라 접근 이유를 자세히 표기</string>

 

Android 이미지 업로드 방법

Android는 iOS와 비교했을 때 굉장히 까다롭습니다. 우선 webview_flutter 패키지를 이용해서 하이브리드 앱을 만들었을 경우(다른 패키지는 잘 모르겠습니다..) <input type = 'file'> 태그를 클릭 시 아무런 반응이 없습니다.

 

구글링을 하다가 발견한 패키지가 바로 image_picker입니다.

 

image_picker | Flutter package

Flutter plugin for selecting images from the Android and iOS image library, and taking new pictures with the camera.

pub.dev

지금 포스팅 시점으로 가장 최신 버전은 1.1.2버전 입니다.

 

우선 제가 사용한 방법은 Flutter와  Webview에서 보여줄 Web의 JavaScript간의 통신을 통해서 이미지를 선택하고 업로드하는 방법입니다.

 

1. JavaScript에서 Flutter로 메시지 보내기

먼저, HTML 페이지에서 파일 선택기를 열기 위해 JavaScript에서 Flutter로 메시지를 보냅니다.

 

function openFilePicker() {
    FilePicker.postMessage('open');
}

 

2. Flutter에서 Message 받기

Flutter에서 JavascriptChannel을 통해 메시지를 전달받고, 그 메시지가 Javascript에서 보낸 message와 같을 경우 갤러리에 접근합니다.

 

@override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Flutter WebView Example'),
      ),
      body: WebView(
        initialUrl: 'about:blank',
        javascriptMode: JavascriptMode.unrestricted,
        javascriptChannels: <JavascriptChannel>{
          JavascriptChannel(
            name: 'FilePicker',
            onMessageReceived: (JavascriptMessage message) {
              if (message.message == 'open') {
                getImage(ImageSource.gallery);
              }
            },
          ),
        },
      ),
    );
  }

 

3. 갤러리에서 Image를 선택해서 Image를 Javascript로 전송

Flutter에서 Image를 File형태로 Javascript로 보내는 방법은 불가능합니다. 그래서 선택한 Image 파일을 base64 인코딩을 이용하여 String으로 만들어 주고 그 Webview가 초기화될 때 Flutter에서 Javascript의 함수를 실행시켜 줍니다.

 

Future<void> getImage(ImageSource imageSource) async {
    final XFile? pickedFile = await picker.pickImage(source: imageSource);
    if (pickedFile != null) {
      final Uint8List bytes = await pickedFile.readAsBytes();
      final String base64 = base64Encode(bytes);

      controller.runJavascript('handleFileUpload("data:image/jpeg;base64,$base64")');
    }
}

 

4.flutter에서 base64로 인코딩 된 이미지를 받아서 다시 이미지 파일로 변환

JavaScript에서 base64 문자열을 파일 객체로 변환한 후, 서버에 업로드합니다.

 

function handleFileUpload(base64String) {
    var file = base64ToFile(base64String);
    uploadFile(file);
}

// flutter에서 base64로 인코딩된 이미지를 받아서 다시 이미지 파일로 변환
function base64ToFile(base64String) {
    // Base64 문자열에서 실제 바이너리 데이터 부분을 분리합니다.
    var byteString = atob(base64String.split(',')[1]);
    
    // Base64 문자열에서 MIME 타입을 추출합니다.
    var mimeString = base64String.split(',')[0].split(':')[1].split(';')[0];
    
    // 바이너리 데이터를 저장할 ArrayBuffer를 생성합니다.
    var ab = new ArrayBuffer(byteString.length);
    
    // ArrayBuffer를 기반으로 Uint8Array를 생성합니다.
    var ia = new Uint8Array(ab);

    // byteString의 각 문자를 해당하는 바이트 값으로 변환하여 Uint8Array에 저장합니다.
    for (var i = 0; i < byteString.length; i++) {
        ia[i] = byteString.charCodeAt(i);
    }

    // Uint8Array를 사용하여 Blob 객체를 생성합니다.
    var blob = new Blob([ab], { type: mimeString });

    // Blob 객체를 사용하여 File 객체를 생성합니다.
    var file = new File([blob], "filename.png", { type: mimeString });

    return file;
}

 

이 과정을 통해 Flutter와 WebView를 사용하여 이미지를 선택하고 서버로 업로드할 수 있습니다. 이 방법을 활용하면 Android와 iOS에서 모두 원활하게 이미지 업로드 기능을 구현할 수 있습니다.

728x90

댓글