Android WebView: скачивайте файлы, как это делают браузеры
Я работаю над приложением для Android с веб-представлением, указывающим на динамический веб-сайт другой команды.
Когда я загружаю файл (в основном динамически перенаправляемый PDF и ZIP), все, что я получаю, это файл в папке загрузок, содержащий некоторый HTML-код с сообщением типа "пользователю не разрешено читать файл", независимо от того, как я выполняю загрузку, я пытался:
- Менеджер загрузки
- Намерение (позволить внешнему браузеру управлять загрузкой)
- "от руки" (AsyncTask и httpconnection...)
все с одинаковыми результатами.
Навигация с помощью обычных загрузок в браузерах работает нормально как на настольных ПК, так и на устройствах Android и iOS.
Почему веб-просмотр не должен иметь доступ к файлам?
Может быть проблема сессия? http порт?
Мне действительно нужны идеи...
Еще один совет: при загрузке файла дважды по одной и той же ссылке ссылка будет перенаправлена на один и тот же файл, но в результате появятся два разных имени файла...
РЕДАКТИРОВАТЬ: вместо того, чтобы указывать веб-вид на веб-приложение, я попытался указать на общую веб-страницу с перенаправлением ссылки, чтобы загрузить другой файл, ну, просто, он работает.
Вот webview.setDownloadListener - onDownloadStart()
параметры:
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
Вот код
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();
}
}
РЕДАКТИРОВАТЬ II
Вот код, который я использую при попытке загрузить файлы "вручную":
onDownloadStart (), где я вызываю 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){
}
}
и это 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);
}
взято из Как скачать PDF с динамического URL в веб-просмотре
Thanx
2 ответа
Наконец я решил поискать DownloadHandler из кода Android Stock Browser. Единственным заметным недостатком в моем коде был файл cookie (!!!).
Вот моя последняя рабочая версия (метод DownloadManager):
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();
}
});
С помощью этой опции мне удалось загрузить полные файлы, загрузка работала с другими опциями, но документы выглядели пустыми, особенно при использовании сеанса.
Добавьте ниже строки в AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
Добавить DownloadListener в ваш WebView
Попробуйте этот код
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();
}
});
спасибо jc за ответ, который вы пропустили ); в конце кода..
Также вы можете использовать:
mWebView.setDownloadListener(new DownloadListener() {
public void onDownloadStart(String url, String userAgent,
String contentDisposition, String mimetype,
long contentLength) {
Intent i = new Intent(Intent.ACTION_VIEW);
i.setData(Uri.parse(url));
startActivity(i);
}});
AsyncTask для ручного получения имени файла (вы можете добавить дополнительный код в этот класс):
private static class getFileNameAsync extends AsyncTask<String, Void, String> {
@Override
protected void onPreExecute() {
super.onPreExecute();
}
@Override
protected String doInBackground(String... params) {
URL url;
String filename = null;
HttpURLConnection conn = null;
try {
url = new URL(params[0]);
conn = (HttpURLConnection) url.openConnection();
conn.connect();
conn.setInstanceFollowRedirects(false);
try {
for(int i = 0; i < 100; i++)
{
String stringURL = conn.getHeaderField("Location");
if (stringURL != null) {
url = new URL(stringURL);
conn = (HttpURLConnection) url.openConnection();
conn.connect();
conn.setInstanceFollowRedirects(false);
} else {
i = 100;
}
}
} catch (Exception e) {
e.printStackTrace();
}
String depo = conn.getHeaderField("Content-Disposition");
if (depo != null) {
String depoSplit[] = depo.split(";");
int size = depoSplit.length;
for(int i = 0; i < size; i++)
{
if(depoSplit[i].startsWith("filename="))
{
filename = depoSplit[i].replaceFirst("(?i)^.*filename=\"?([^\"]+)\"?.*$", "$1").trim();
i = size;
}
}
}
} catch (MalformedURLException e){
e.printStackTrace();
} catch (ProtocolException e){
e.printStackTrace();
} catch (FileNotFoundException e){
e.printStackTrace();
} catch (IOException e){
e.printStackTrace();
} catch (Exception e){
e.printStackTrace();
} finally {
if (conn != null)
conn.disconnect();
}
return filename;
}
@Override
protected void onPostExecute(String filename) {
super.onPostExecute(filename);
}
}
DownloadListener (это стабильная версия, не меняйте ее):
mWebView.setDownloadListener(new DownloadListener() {
@Override
public void onDownloadStart(String url,
String userAgent,
String contentDisposition,
String mimetype,
long contentLength) {
if(Build.VERSION.SDK_INT >=23){
Context nContext = MainActivity.this.getApplicationContext();
if(nContext.checkSelfPermission(android.Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED){
MainActivity.this.requestPermissions(new String[]{android.Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
return;
}
}
String fileName = "";
url = url.replace(" ", "%20");
AsyncTask<String, Void, String> asyncTask = new getFileNameAsync();
asyncTask.execute(url);
try {
fileName = asyncTask.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
} catch (CancellationException e) {
e.printStackTrace();
}
if ((fileName == null) || (fileName.hashCode() == "".hashCode())) {
fileName = URLUtil.guessFileName(url, contentDisposition, mimetype);
}
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
request.setMimeType(mimetype);
String cookies = CookieManager.getInstance().getCookie(url);
request.addRequestHeader("Cookie", cookies);
request.addRequestHeader("User-Agent", userAgent);
request.setDescription("Downloading File");
request.setTitle(fileName);
request.allowScanningByMediaScanner();
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName);
DownloadManager downloadManager = (DownloadManager) getSystemService(DOWNLOAD_SERVICE);
if (downloadManager != null) {
downloadManager.enqueue(request);
}
Toast.makeText(getApplicationContext(), "Downloading File", Toast.LENGTH_LONG).show();
}
});
Обратите внимание - вы также должны добавить заголовок запроса "Referer" - без него некоторые сайты просто не позволят вам скачивать файлы.