我一直在努力从WebView上传文件。我在谷歌上搜索并实施了所有建议的解决方案(例如这篇SO帖子),但没有一个可行。
我有一个HTML页面与以下代码上传文件。
<form method="POST" enctype="multipart/form-data">
File to upload: <input type="file" name="uploadfile">
<input type="submit" value="Press to Upload..."> to upload the file!
</form>
它在桌面浏览器如Firefox和内置浏览器中运行良好
的模拟器/ AVD,即,当我点击“浏览…”按钮渲染
元素,浏览器打开一个对话框
框,在那里我可以选择一个文件上传。
然而,在android 3.0模拟器/ AVD中,当我点击“选择”
文件”,什么都没有发生,没有文件对话框被打开!
2019:这段代码对我有效(在android 5 - 9上测试)。
package com.example.filechooser;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.net.http.SslError;
import android.os.Bundle;
import android.webkit.SslErrorHandler;
import android.webkit.ValueCallback;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import android.webkit.WebViewClient;
public class MainActivity extends Activity {
// variables para manejar la subida de archivos
private final static int FILECHOOSER_RESULTCODE = 1;
private ValueCallback<Uri[]> mUploadMessage;
// variable para manejar el navegador empotrado
WebView mainWebView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// instanciamos el webview
mainWebView = findViewById(R.id.main_web_view);
// establecemos el cliente interno para que la navegacion no se salga de la aplicacion
mainWebView.setWebViewClient(new MyWebViewClient());
// establecemos el cliente chrome para seleccionar archivos
mainWebView.setWebChromeClient(new MyWebChromeClient());
// configuracion del webview
mainWebView.getSettings().setJavaScriptEnabled(true);
// cargamos la pagina
mainWebView.loadUrl("https://example.com");
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
// manejo de seleccion de archivo
if (requestCode == FILECHOOSER_RESULTCODE) {
if (null == mUploadMessage || intent == null || resultCode != RESULT_OK) {
return;
}
Uri[] result = null;
String dataString = intent.getDataString();
if (dataString != null) {
result = new Uri[]{ Uri.parse(dataString) };
}
mUploadMessage.onReceiveValue(result);
mUploadMessage = null;
}
}
// ====================
// Web clients classes
// ====================
/**
* Clase para configurar el webview
*/
private class MyWebViewClient extends WebViewClient {
// permite la navegacion dentro del webview
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
}
/**
* Clase para configurar el chrome client para que nos permita seleccionar archivos
*/
private class MyWebChromeClient extends WebChromeClient {
// maneja la accion de seleccionar archivos
@Override
public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
// asegurar que no existan callbacks
if (mUploadMessage != null) {
mUploadMessage.onReceiveValue(null);
}
mUploadMessage = filePathCallback;
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("*/*"); // set MIME type to filter
MainActivity.this.startActivityForResult(Intent.createChooser(i, "File Chooser"), MainActivity.FILECHOOSER_RESULTCODE );
return true;
}
}
}
希望能帮到你。
解决方案:https://github.com/delight-im/Android-AdvancedWebView
崩裂剂解决方案:
activity和fragment的区别只在onActivityResult中:
片段:
lateinit var webViewGlobal: AdvancedWebView private set
class WebViewFragment : Fragment(), AdvancedWebView.Listener {
private lateinit var binding: FragmentWebViewBinding
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
binding = FragmentWebViewBinding.inflate(inflater)
webViewGlobal = binding.webWiew
return binding.root
}
}
活动:
class MainActivity : AppCompatActivity() {
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
webViewGlobal.onActivityResult(requestCode, resultCode, data)
super.onActivityResult(requestCode, resultCode, data)
}
}
Android 11:
<application
...
android:requestLegacyExternalStorage="true"
...
/>
清单:
<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
在棒棒糖5.0中,谷歌添加了一个官方方法WebChromeClient.onShowFileChooser。它们甚至提供了一种自动生成文件选择器意图的方法,以便它使用输入接受mime类型。
public class MyWebChromeClient extends WebChromeClient {
// reference to activity instance. May be unnecessary if your web chrome client is member class.
private MyActivity activity;
public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
// make sure there is no existing message
if (myActivity.uploadMessage != null) {
myActivity.uploadMessage.onReceiveValue(null);
myActivity.uploadMessage = null;
}
myActivity.uploadMessage = filePathCallback;
Intent intent = fileChooserParams.createIntent();
try {
myActivity.startActivityForResult(intent, MyActivity.REQUEST_SELECT_FILE);
} catch (ActivityNotFoundException e) {
myActivity.uploadMessage = null;
Toast.makeText(myActivity, "Cannot open file chooser", Toast.LENGTH_LONG).show();
return false;
}
return true;
}
}
public class MyActivity extends ... {
public static final int REQUEST_SELECT_FILE = 100;
public ValueCallback<Uri[]> uploadMessage;
protected void onActivityResult(int requestCode, int resultCode, Intent data){
if (requestCode == REQUEST_SELECT_FILE) {
if (uploadMessage == null) return;
uploadMessage.onReceiveValue(WebChromeClient.FileChooserParams.parseResult(resultCode, data));
uploadMessage = null;
}
}
}
}
对于KitKat之前的Android版本,其他答案中提到的私有方法是有效的。我还没有为KitKat(4.4)找到一个好的解决方案。
Android 8的Kotlin解决方案:
private var mUploadMessage: ValueCallback<Uri>? = null
private var uploadMessage: ValueCallback<Array<Uri>>? = null
常量:
const val FILECHOOSER_RESULTCODE = 1
const val REQUEST_SELECT_FILE = 100
WebView设置:
webView.webChromeClient = object : WebChromeClient() {
override fun onPermissionRequest(request: PermissionRequest?) {
Log.d("MainActivity", "onPermissionRequest")
requestPermission(request)
}
// For Android 3.0+
fun openFileChooser(uploadMsg: ValueCallback<*>, acceptType: String) {
mUploadMessage = uploadMsg as ValueCallback<Uri>
val i = Intent(Intent.ACTION_GET_CONTENT)
i.addCategory(Intent.CATEGORY_OPENABLE)
i.type = "*/*"
this@MainActivity.startActivityForResult(
Intent.createChooser(i, "File Browser"),
FILECHOOSER_RESULTCODE)
}
//For Android 4.1
fun openFileChooser(uploadMsg: ValueCallback<Uri>, acceptType: String, capture: String) {
mUploadMessage = uploadMsg
val i = Intent(Intent.ACTION_GET_CONTENT)
i.addCategory(Intent.CATEGORY_OPENABLE)
i.type = "image/*"
this@MainActivity.startActivityForResult(Intent.createChooser(i, "File Chooser"), FILECHOOSER_RESULTCODE)
}
protected fun openFileChooser(uploadMsg: ValueCallback<Uri>) {
mUploadMessage = uploadMsg
val intent = Intent(Intent.ACTION_GET_CONTENT)
intent.addCategory(Intent.CATEGORY_OPENABLE)
intent.type = "*/*"
startActivityForResult(Intent.createChooser(intent, "File Chooser"), FILECHOOSER_RESULTCODE)
}
override fun onShowFileChooser(webView: WebView?, filePathCallback: ValueCallback<Array<Uri>>?, fileChooserParams: FileChooserParams?): Boolean {
uploadMessage?.onReceiveValue(null)
uploadMessage = null
uploadMessage = filePathCallback
val intent = fileChooserParams!!.createIntent()
try {
startActivityForResult(intent, REQUEST_SELECT_FILE)
} catch (e: ActivityNotFoundException) {
uploadMessage = null
Toast.makeText(applicationContext, "Cannot Open File Chooser", Toast.LENGTH_LONG).show()
return false
}
return true
}
}
onactivityresult部分:
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
if (requestCode === REQUEST_SELECT_FILE) {
if (uploadMessage == null)
return
print("result code = " + resultCode)
var results: Array<Uri>? = WebChromeClient.FileChooserParams.parseResult(resultCode, data)
uploadMessage?.onReceiveValue(results)
uploadMessage = null
}
} else if (requestCode === FILECHOOSER_RESULTCODE) {
if (null == mUploadMessage)
return
// Use MainActivity.RESULT_OK if you're implementing WebView inside Fragment
// Use RESULT_OK only if you're implementing WebView inside an Activity
val result = if (intent == null || resultCode !== RESULT_OK) null else intent.data
mUploadMessage?.onReceiveValue(result)
mUploadMessage = null
} else
Toast.makeText(applicationContext, "Failed to Upload Image", Toast.LENGTH_LONG).show()
}
请注意,我们的意图变量称为“数据”。