[Android] webview download file async 웹뷰에서 파일 다운로드 (include sdk23 android 6.0 marshmallow)

북마크 추가

기본 webview에서는 다운로드 링크를 눌러도 아무 반응도 없습니다. 따로 구현을 해줘야 됩니다.

 

1. permission

 

파일다운로드를 위해서는 WRITE_EXTERNAL_STORAGE permission이 필요합니다.

 

targetSdkVersion < 23 인경우는 아래와 같이 처리하면 됩니다.


androidManifest.xml


<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />



 android 6 (sdk23)부터는 permission 정책이 변경 되어서 위의 설정만 할 경우

 

java.lang.SecurityException: No permission to write to /storage/emulated/0/Download/foo.jpg: Neither user 10386 nor current process has android.permission.WRITE_EXTERNAL_STORAGE.

 

에러가 발생하고 앱이 죽습니다.

 

권한 주는 방법은 아래에서 설명하도록 하겠습니다.

 

2. DownloadListener 구현

 

public class MainActivity extends Activity {


private WebView mWebView;

protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mWebView = (WebView) findViewById(R.id.activity_trandent_webview);
.
.
.
.

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
mWebView.setDownloadListener(new DownloadListener() {

@Override
public void onDownloadStart(String url, String userAgent, String contentDisposition, String mimeType, long contentLength) {

try {
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
request.setMimeType(mimeType);
request.addRequestHeader("User-Agent", userAgent);
request.setDescription("Downloading file");
String fileName = contentDisposition.replace("inline; filename=", "");
fileName = fileName.replaceAll("\"", "");
request.setTitle(fileName);
request.allowScanningByMediaScanner();
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName);
DownloadManager dm = (DownloadManager) getSystemService(DOWNLOAD_SERVICE);
dm.enqueue(request);
Toast.makeText(getApplicationContext(), "Downloading File", Toast.LENGTH_LONG).show();
} catch (Exception e) {

if (ContextCompat.checkSelfPermission(MainActivity.this,
android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
// Should we show an explanation?
if (ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this,
android.Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
Toast.makeText(getBaseContext(), "첨부파일 다운로드를 위해\n동의가 필요합니다.", Toast.LENGTH_LONG).show();
ActivityCompat.requestPermissions(MainActivity.this, new String[]{android.Manifest.permission.WRITE_EXTERNAL_STORAGE},
110);
} else {
Toast.makeText(getBaseContext(), "첨부파일 다운로드를 위해\n동의가 필요합니다.", Toast.LENGTH_LONG).show();
ActivityCompat.requestPermissions(MainActivity.this, new String[]{android.Manifest.permission.WRITE_EXTERNAL_STORAGE},
110);
}
}
}
}
});
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
}

}

 

DownloadListener에 onDownloadStart를 override 합니다.

 

content-disposition 이 attachment; filename="" 으로 되어 있다면

 

request.setTitle(URLUtil.guessFileName(url,contentDisposition,mimeType));

inline; filename="" 으로 되어 있다면

 

String fileName = contentDisposition.replace("inline; filename=", "");
fileName = fileName.replaceAll("\"", "");
request.setTitle(fileName);

 

으로 작성합니다.

 

 request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName);

 

이 부분의 fileName도 위와같이  URLUtil.guessFileName(url,contentDisposition,mimeType) or fileName으로 넘겨줍니다.

 

 *  sdk23 ( android 6 marshmallow)에서는 위 코드의 try부분은 꼭 try catch로 감싸줘야 합니다. 감싸주지 않으면 SecurityException을 발생시키며 어플이 죽습니다.

 

정상동작을 위해서는 별도의 permission을 요청해야 됩니다.

 

catch에서 permission을 요청합니다.

 

if (ContextCompat.checkSelfPermission(MainActivity.this,
android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) { //1. if no permission
if (ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this,
android.Manifest.permission.WRITE_EXTERNAL_STORAGE)) { //3. if user click deny, re-requestPermission
Toast.makeText(getBaseContext(), "첨부파일 다운로드를 위해\n동의가 필요합니다.", Toast.LENGTH_LONG).show();
ActivityCompat.requestPermissions(MainActivity.this, new String[]{android.Manifest.permission.WRITE_EXTERNAL_STORAGE},
110);
} else { // 2.first requestPermission : yes or no
Toast.makeText(getBaseContext(), "첨부파일 다운로드를 위해\n동의가 필요합니다.", Toast.LENGTH_LONG).show();
ActivityCompat.requestPermissions(MainActivity.this, new String[]{android.Manifest.permission.WRITE_EXTERNAL_STORAGE},
110);
}
}

1. 퍼미션이 없으면 else에서 유저에게 WRITE_EXTERNAL_STORAGE 권한을 요청합니다.

2. 여기서 권한 허용을 하느냐 거부를 하느냐를 선택하는데 허용 하는경우 이제 정상적으로 파일다운로드가 가능해지고 거부할 경우 파일 다운로드를 시도할때마다 3번으로 가게 됩니다.

3. 이부분역시 권한을 요청하는 부분으로  동의를 하면 파일다운로드가 가능해지고 거부할 경우 재 시도시 권한 요청을 합니다.

 

 

결과 -

 

1. 링크 클릭

 

 

 

2. permission 요청

 

 

 

3. 다운로드 성공

 

 

 

 

 

 

HKH
2016-05-17 17:51
SHARE
댓글

url을 decoding 해줘야 될것같습니다.
요새 바빠서 테스트를 해볼 시간이 없는데 시간나는데로 확인 해 보도록 하겠습니다.
HKH

한글파일인 경우 깨지는 현상이 있는데 어떻게 수정해야하나요?
이승재
keyboard_arrow_left
keyboard_arrow_up