Android webview: download files like browsers do
I'm working on an Android app with a webview pointing to a dynamic website by another team.
When i download a file (mostly dynamically redirected PDF and ZIP) all i get is a file in the downloads folder containing some HTML code with a message like "user not allowed to read the file", no matter how i implement the download, i tried:
- DownloadManager
- Intent (letting an external browser to manage the download)
- "by hand" (AsyncTask and httpconnection...)
all with the same results.
Navigating with normal browsers downloads work fine, both on desktop PC, android and iOS devices.
Why webview shouldn't have access to files?
May be a session issue? http port?
I really need some ideas...
Another tip: when downloading twice a file from the same link, the link will redirect to the same file but resulting in two different filenames...
EDIT: Instead of pointing the webView to the web-app, i tried to point to a common webpage with a link-redirect to download another file, well, simply it works.
Here are the webview.setDownloadListener - onDownloadStart()
parameters:
userAgent=Mozilla/5.0 (Linux; Android 4.4.2; Nexus 7 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/30.0.0.0 Safari/537.36
contentDisposition=attachment;
filename=correct_filename.pdf,
url=http://www.xxx.xx/site/downloadfile.wplus?REDIRECTFILE=D-507497120&ID_COUNTOBJ=ce_5_home&TYPEOBJ=CExFILE&LN=2
mimeType=application/octet-stream
Here's some code
wv.getSettings().setSupportMultipleWindows(true);
wv.getSettings().setJavaScriptCanOpenWindowsAutomatically(true);
wv.getSettings().setAllowFileAccess(true);
wv.getSettings().setJavaScriptEnabled(true);
wv.getSettings().setBuiltInZoomControls(true);
wv.getSettings().setDisplayZoomControls(false);
wv.getSettings().setLoadWithOverviewMode(true);
wv.getSettings().setUseWideViewPort(true);
wv.setDownloadListener(new DownloadListener() {
@Override
public void onDownloadStart(String url, String userAgent, String contentDisposition, String mimetype, long contentLength){
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
request.setDescription("Download file...");
request.setTitle(URLUtil.guessFileName(url, contentDisposition, mimetype));
request.allowScanningByMediaScanner();
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED); //Notify client once download is completed!
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, URLUtil.guessFileName(url, contentDisposition, mimetype));
DownloadManager dm = (DownloadManager) getSystemService(DOWNLOAD_SERVICE);
dm.enqueue(request);
Toast.makeText(getApplicationContext(), "Downloading File", Toast.LENGTH_LONG).show();
}
}
EDIT II
Here's the code i'm using when trying to download files "by hand":
onDownloadStart() is where i call downloadFileAsync():
public void onDownloadStart(String url, String userAgent, String contentDisposition, String mimeType, long contentLength) {
String fileName;
try {
fileName = URLUtil.guessFileName(url, contentDisposition, mimeType);
downloadFileAsync(url, fileName);
}catch (Exception e){
}
}
and this is the AsyncTask:
private void downloadFileAsync(String url, String filename){
new AsyncTask<String, Void, String>() {
String SDCard;
@Override
protected void onPreExecute() {
super.onPreExecute();
}
@Override
protected String doInBackground(String... params) {
try {
URL url = new URL(params[0]);
HttpURLConnection urlConnection = null;
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setRequestMethod("GET");
urlConnection.setDoOutput(true);
urlConnection.connect();
int lengthOfFile = urlConnection.getContentLength();
//SDCard = Environment.getExternalStorageDirectory() + File.separator + "downloads";
SDCard = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)+"";
int k = 0;
boolean file_exists;
String finalValue = params[1];
do {
if (k > 0) {
if (params[1].length() > 0) {
String s = params[1].substring(0, params[1].lastIndexOf("."));
String extension = params[1].replace(s, "");
finalValue = s + "(" + k + ")" + extension;
} else {
String fileName = params[0].substring(params[0].lastIndexOf('/') + 1);
String s = fileName.substring(0, fileName.lastIndexOf("."));
String extension = fileName.replace(s, "");
finalValue = s + "(" + k + ")" + extension;
}
}
File fileIn = new File(SDCard, finalValue);
file_exists = fileIn.exists();
k++;
} while (file_exists);
File file = new File(SDCard, finalValue);
FileOutputStream fileOutput = null;
fileOutput = new FileOutputStream(file, true);
InputStream inputStream = null;
inputStream = urlConnection.getInputStream();
byte[] buffer = new byte[1024];
int count;
long total = 0;
while ((count = inputStream.read(buffer)) != -1) {
total += count;
//publishProgress(""+(int)((total*100)/lengthOfFile));
fileOutput.write(buffer, 0, count);
}
fileOutput.flush();
fileOutput.close();
inputStream.close();
} catch (MalformedURLException e){
} catch (ProtocolException e){
} catch (FileNotFoundException e){
} catch (IOException e){
} catch (Exception e){
}
return params[1];
}
@Override
protected void onPostExecute(final String result) {
}
}.execute(url, filename);
}
taken from How to download a PDF from a dynamic URL in a webview
Thanx
Finally i decided to look for the DownloadHandler from the Android Stock Browser code. The only noticeable lack in my code was cookie (!!!).
Here's my final working version (DownloadManager method):
wv.setDownloadListener(new DownloadListener() {
@Override
public void onDownloadStart(String url, String userAgent, String contentDisposition, String mimeType, long contentLength) {
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
request.setMimeType(mimeType);
//------------------------COOKIE!!------------------------
String cookies = CookieManager.getInstance().getCookie(url);
request.addRequestHeader("cookie", cookies);
//------------------------COOKIE!!------------------------
request.addRequestHeader("User-Agent", userAgent);
request.setDescription("Downloading file...");
request.setTitle(URLUtil.guessFileName(url, contentDisposition, mimeType));
request.allowScanningByMediaScanner();
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, URLUtil.guessFileName(url, contentDisposition, mimeType));
DownloadManager dm = (DownloadManager) getSystemService(DOWNLOAD_SERVICE);
dm.enqueue(request);
Toast.makeText(getApplicationContext(), "Downloading File", Toast.LENGTH_LONG).show();
}
});
With this option I managed to download complete files, the download worked with other options but the documents appeared empty, especially when a session is being used.
Add below lines to AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
Add DownloadListener to your WebView
Try this code
wv.setDownloadListener(new DownloadListener() {
@Override
public void onDownloadStart(final String url, final String userAgent, String contentDisposition, String mimetype, long contentLength) {
//Checking runtime permission for devices above Marshmallow.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (checkSelfPermission(android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
== PackageManager.PERMISSION_GRANTED) {
Log.v(TAG, "Permission is granted");
downloadDialog(url, userAgent, contentDisposition, mimetype);
} else {
Log.v(TAG, "Permission is revoked");
//requesting permissions.
ActivityCompat.requestPermissions(PortalActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
}
} else {
//Code for devices below API 23 or Marshmallow
Log.v(TAG, "Permission is granted");
downloadDialog(url, userAgent, contentDisposition, mimetype);
}
}
});
//downloadDialog Method
public void downloadDialog(final String url, final String userAgent, String contentDisposition, String mimetype) {
//getting filename from url.
final String filename = URLUtil.guessFileName(url, contentDisposition, mimetype);
//alertdialog
AlertDialog.Builder builder = new AlertDialog.Builder(this);
//title of alertdialog
builder.setTitle(R.string.download_title);
//message of alertdialog
builder.setMessage(getString(R.string.download_file) + ' ' + filename);
//if Yes button clicks.
builder.setPositiveButton(getString(R.string.yes), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
//DownloadManager.Request created with url.
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
//cookie
String cookie = CookieManager.getInstance().getCookie(url);
//Add cookie and User-Agent to request
request.addRequestHeader("Cookie", cookie);
request.addRequestHeader("User-Agent", userAgent);
//file scanned by MediaScannar
request.allowScanningByMediaScanner();
//Download is visible and its progress, after completion too.
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
//DownloadManager created
DownloadManager downloadManager = (DownloadManager) getSystemService(DOWNLOAD_SERVICE);
//Saving files in Download folder
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, filename);
//download enqued
downloadManager.enqueue(request);
}
});
builder.setNegativeButton("No", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
//cancel the dialog if Cancel clicks
dialog.cancel();
mWebView.goBack();
}
});
//alertdialog shows.
builder.show();
}
wv.setDownloadListener(new DownloadListener() {
@Override
public void onDownloadStart(String url, String userAgent, String
contentDisposition, String mimeType, long contentLength) {
DownloadManager.Request request = new
DownloadManager.Request(Uri.parse(url));
request.setMimeType(mimeType);
//------------------------COOKIE!!------------------------
String cookies = CookieManager.getInstance().getCookie(url);
request.addRequestHeader("cookie", cookies);
//------------------------COOKIE!!------------------------
request.addRequestHeader("User-Agent", userAgent);
request.setDescription("Downloading file...");
request.setTitle(URLUtil.guessFileName(url, contentDisposition, mimeType));
request.allowScanningByMediaScanner();
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, URLUtil.guessFileName(url, contentDisposition, mimeType));
DownloadManager dm = (DownloadManager) getSystemService(DOWNLOAD_SERVICE);
dm.enqueue(request);
Toast.makeText(getApplicationContext(), "Downloading File", Toast.LENGTH_LONG).show();
}
});
thanks j.c for your answer you missed ); at end of code..