[flutter] InAppWebView file download (Android 버전) 에러 해결과정 [해결]

like0·2022년 3월 31일
0

지금 notification 클릭하면 파일 안열리는데,, 어디서 잘못된 것인지 찾아가기 위해 작업 과정을 기록해본다. 해결 !!!

Android integration

  • 내가 작업하는 플러터 환경은 v2 embedding이어서 추가적으로 필요한 작업은 없었지만, 프로젝트가 플러터 v1.12 이전이라면, 해당 과정을 먼저 거쳐야 한다.

notification을 클릭함으로써 다운로드된 파일을 열기 위해서, AndroidManifest.xml 파일에 다음 코드를 추가해주어야 한다.

<provider
    android:name="vn.hunghd.flutterdownloader.DownloadedFileProvider"
    android:authorities="${applicationId}.flutter_downloader.provider"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/provider_paths"/>
</provider>

- 다운로드 파일을 external strage(어플이 permission을 가진 곳)에 저장해야 한다.

- 다운로드된 파일은 파일 확장자를 읽을 수 있는 어플이 있을 때만 열 수 있다.

Usage

  1. 패키지 import 한다. (flutter_downloader Installing)
import 'package:flutter_downloader/flutter_downloader.dart';
  1. 초기화하기

해당 plugin은 초기화가 필수이다.

//main함수 내에서 했다.
WidgetsFlutterBinding.ensureInitialized();
await FlutterDownloader.initialize(
  debug: true // optional: set false to disable printing logs to console
);
  1. download task 만들기
    // 나는 이 때, InAppWebView widget 내에서 실행했기 때문에,
    // InAppWebView의 함수인 onDownloadStart를 통해 실행했다.
//공식 문서에 나와있는 코드
final taskId = await FlutterDownloader.enqueue(
  url: 'your download link',
  savedDir: 'the path of directory where you want to save downloaded files',
  showNotification: true, // show download progress in status bar (for Android)
  openFileFromNotification: true, // click on notification to open downloaded file (for Android)
);

//실제 사용된 코드의 일부
InAppWebView (
	...
    
    onDownloadStart: (controller, url) async {
    	final taskId = await FlutterDownloader.enqueue(
        url: url.toString(),
        savedDir: // 이부분에서도 시간 많이 썼는데 뒤에서 마저 정리할 것임.,
        showNotification: true, // show download progress in status bar (for Android)
        openFileFromNotification: true, // click on notification to open downloaded file (for Android)
        saveInPublicStorage: true, //이거 꼭꼭 있어야한다. 이거 없어서 파일 저장이 제대로 안되었다.
);

    }
    
    ...
)

3-1. savedDir은 다운로드 된 파일이 저장되는 path를 나타내는 곳이다.

import 'package:path_provider/path_provider.dart';
  • example (이 외에도 다양한 예시가 있다.)
Directory tempDir = await getTemporaryDirectory();
String tempPath = tempDir.path;
Directory appDocDir = await getApplicationDocumentsDirectory();
String appDocPath = appDocDir.path;

이런식으로 path를 구하는 것이다.
나는 아래처럼 path를 설정해서, 3번 download task의 savedDir에 해당 path를 할당했다.

var externalStorageDirPath;
final directory = await getExternalStorageDirectory();
externalStorageDirPath = directory?.path;

(공식 문서에 보면 external storage에 저장을 해두어야 한다고 되어있는데,, 내가 여기서 잘못 해서 notification에서 파일이 안열리는 것 같다고 생각이 된다..)

  1. Update download progress
FlutterDownloader.registerCallback(callback); // callback is a top-level or static function
  • UI는 main isolate으로 렌더링되지만, download event는 backgound isolate에서 발생한다. 2개의 iolate간에 통신을 처리해야한다. 아래가 해당 코드이다.
  • 링크 에 있는 코드가 도움이 되었다. (해당 코드를 어디에 위치 시켜야하는지)
ReceivePort _port = ReceivePort();

@override
void initState() {
	super.initState();

	IsolateNameServer.registerPortWithName(_port.sendPort, 'downloader_send_port');
	_port.listen((dynamic data) {
		String id = data[0];
		DownloadTaskStatus status = data[1];
		int progress = data[2];
		setState((){ });
	});

	FlutterDownloader.registerCallback(downloadCallback);
}

@override
void dispose() {
	IsolateNameServer.removePortNameMapping('downloader_send_port');
	super.dispose();
}

@pragma('vm:entry-point')
static void downloadCallback(String id, DownloadTaskStatus status, int progress) {
	final SendPort send = IsolateNameServer.lookupPortByName('downloader_send_port');
	send.send([id, status, progress]);
}

@pragma('vm:entry-point') 는 반드시 callback 함수 위에 위치해야한다.

원인과 해결방법

- 다운로드 파일을 external strage(어플이 permission을 가진 곳)에 저장해야 한다.

- 다운로드된 파일은 파일 확장자를 읽을 수 있는 어플이 있을 때만 열 수 있다.

이 요소가 중요한것 같다.

profile
배우고 성장하는 개발자가 되기!

0개의 댓글