Android webview clicks <input type="file"> to select the implementation of the file

Starting from Android 5.0 (the previous version has not been tested, I just learned this information from some information), the implementation of opening files of <input type="file"> in webview has changed, and there is no response when clicking. WebChromeClient needs to be reloaded to handle the operation of opening files.

Because the version is constantly updated and iterated, deprecated functions will be used in some implementations on the Internet. The following is my implementation of "2023/4", which uses registerForActivityResult. Compared with startActivityForResult, it is better. It uses callbacks so that it does not need to implement a function in multiple functions.

First, register an ActivityResultLauncher to open a file browser to select a file:

//解决安卓5及以上版本html<input type="file">无响应问题
private ValueCallback<Uri[]> checkedFile;
private ActivityResultLauncher<String[]> explorer;

//在Activity.onCreate中实现以下内容,实现打开文件浏览器选择文件的功能
protected void onCreate(Bundle savedInstanceState) {
    ........
    explorer = registerForActivityResult(new ActivityResultContracts.OpenDocument(),
	result -> {
		LOG.debug("Open file `{}`", result);
		if(checkedFile != null) {
			if(result == null) {
				checkedFile.onReceiveValue(null); //result没选择时为null
			} else {
				checkedFile.onReceiveValue(new Uri[]{result});
			}
			checkedFile = null;
		}
	});
    ........
}

Secondly, Android 5 or above, overload WebChromeClient.onShowFileChoose (the old version needs to overload other functions, you can refer to other introductions on the Internet, I think it is not necessary to be compatible below 5, so it is not implemented).

WebChromeClient chromeClient = new WebChromeClient() {
	@Override
	public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, WebChromeClient.FileChooserParams fileChooserParams) {
		if (checkedFile != null) {
			//防止上一次没选文件,直接退出的情况,导致后面选择无法正常工作
			checkedFile.onReceiveValue(null);
		}
		checkedFile = filePathCallback;
		String[] acceptTypes = fileChooserParams.getAcceptTypes();
		LOG.debug("fileChooserParams:{},sdk_int:{}", Arrays.stream(acceptTypes).toArray(), Build.VERSION.SDK_INT);
		if(Build.VERSION.SDK_INT <= Build.VERSION_CODES.N){
			explorer.launch(new String[]{"*/*"});
		} else {
			explorer.launch(acceptTypes);
		}
		return true;
	}
};
........
//设置到webview中
webview.setWebChromeClient(chromeClient);

Note that Android 7 and previous versions are judged in the code, and the fileChooserParams parameter cannot be used to limit the selected file type, otherwise there is no response to the click, and there is no error message. The reason is unknown, so "*/*" is passed here.

One key point: get checkedFile in onShowFileChooser, which is a callback. After the explorer selects the file, call checkedFile.onReceiveValue to pass the result to the webview.

The processing in html is exactly the same as that of ordinary web browsers. The following is an example of obtaining and cropping image files. For the complete content, see this link serverui/file/settings/home.js · master · Zhijian Grid / Zhijian Grid Enterprise Service · GitCode , in the startCropLogo function.

  var file = e.target.files[0];
  if (!/\.(gif|jpg|jpeg|png|bmp|GIF|JPG|PNG)$/.test(e.target.value)) {
    this.$refs.alertDlg.show(this.tags.invalidImg);
    return false
  }
  var reader = new FileReader()
  reader.onload = (e) => {
    var data
    if (typeof e.target.result === 'object') {
      // 把Array Buffer转化为blob 如果是base64不需要
      data = window.URL.createObjectURL(new Blob([e.target.result]))
    } else {
      data = e.target.result
    }
    this.logoOpts.img = data
	this.loading=false;
  }
  // reader.readAsDataURL(file)// 转化为base64
  reader.readAsArrayBuffer(file)// 转化为blob

Guess you like

Origin blog.csdn.net/flyinmind/article/details/130194217