Android开发笔记(一百五十二)H5通过WebView上传图片

上一篇文章介绍了WebView与JS之间的数据交互,其实就是把字符串传来传去,这对文本格式的信息传输来说倒还凑合,倘若要传输图片信息就不管用了。所以,要想让h5网页支持从手机上传图片,还得另外想办法,当然各版本的Android系统也都提供了相应的解决办法。在Android 4.*系统上面,开发者可以重写WebChromeClient的openFileChooser函数;在Android 5.0以上的系统,开发者可以重写WebChromeClient的onShowFileChooser函数。话虽如此,可实际编码的时候,会发现并不容易,因为不但要兼容各种版本的安卓系统,而且要考虑不同操作方式下面的处理步骤。 首先是Android不同系统的适配问题,对于4.*版本要重写openFileChooser方法,对于5.0以上版本要重写onShowFileChooser方法。另外注意二者的回调方式也不一样,4.*的回调参数类型是ValueCallback<Uri>,而5.0以上的回调参数类型是ValueCallback<Uri[]>,因此要声明两个回调参数变量,分别用来保存二者各自的回调信息。相关代码如下所示:

	private static ValueCallback<Uri> mUploadMessage;
	private static ValueCallback<Uri[]> mUploadMessageLollipop;
	
	private class MyWebChromeClient extends WebChromeClient {

		// Android 4.*(包括4.1、4.2、4.3、4.4)
		public void openFileChooser(ValueCallback<Uri> uploadMsg,
				String acceptType, String capture) {
			Log.d(TAG, "openFileChooser 4.*");
			mUploadMessage = uploadMsg;
			openSelectDialog();
		}

		// Android 5.0+(包括5.*、6.0、7.*、8.*)
		@Override
		public boolean onShowFileChooser(WebView webView,
				ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
			Log.d(TAG, "openFileChooser 5.0+");
			mUploadMessageLollipop = filePathCallback;
			openSelectDialog();
			return true;
		}
	}

然后就上传图片这个功能而言,既要支持从手机相册中挑选已有的图片,也要支持现场拍照并即时上传拍摄好的照片。如此一来,就不能仅仅从相册选择文件,而要弹出一个列表对话框,好让用户决定是从相册上传图片,还是当场拍照当场上传。所以接下来得同时实现这两种上传方式,示例代码如下:

	private String mCameraPhotoPath = null;
	
	private void openSelectDialog() {
		// 声明相机的拍照行为
		Intent photoIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
		if (photoIntent.resolveActivity(getPackageManager()) != null) {
			mCameraPhotoPath = "file:" + getExternalFilesDir(Environment.DIRECTORY_PICTURES)
							.toString() + "/" + DateUtil.getNowDateTime("") + ".jpg";
			Log.d(TAG, "photoFile=" + mCameraPhotoPath);
			photoIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.parse(mCameraPhotoPath));
		}
		Intent[] intentArray = new Intent[] { photoIntent };
		// 声明相册的打开行为
		Intent selectionIntent = new Intent(Intent.ACTION_GET_CONTENT);
		selectionIntent.addCategory(Intent.CATEGORY_OPENABLE);
		selectionIntent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
		selectionIntent.setType("image/*");
		// 弹出含相机和相册在内的列表对话框
		Intent chooserIntent = new Intent(Intent.ACTION_CHOOSER);
		chooserIntent.putExtra(Intent.EXTRA_INTENT, selectionIntent);
		chooserIntent.putExtra(Intent.EXTRA_TITLE, "请拍照或选择图片");
		chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentArray);
		startActivityForResult(Intent.createChooser(chooserIntent, "选择图片"), 1);
	}

选择好图片确定后(含拍照和从相册选取),App代码进入到onActivityResult方法内部,开发者在此校验结果代码,根据图片选取形式分别获得具体的图片数据,然后区分4.*系统和5.+系统将图片传给h5页面。下面是onActivityResult方法的处理代码:

	private static final int FILE_SELECT_CODE = 1;
	private int mResultCode = Activity.RESULT_CANCELED;
	
	@Override
	public void onActivityResult(int requestCode, int resultCode, Intent data) {
		Log.d(TAG, "onActivityResult requestCode=" + requestCode + ", resultCode=" + resultCode);
		if (requestCode != FILE_SELECT_CODE
				|| (mUploadMessage == null && mUploadMessageLollipop == null)) {
			super.onActivityResult(requestCode, resultCode, data);
			return;
		}
		mResultCode = resultCode;
		Log.d(TAG, "mCameraPhotoPath=" + mCameraPhotoPath);
		if (resultCode == Activity.RESULT_OK) {
			uploadPhoto(resultCode, data);
		}
	}

	private void uploadPhoto(int resultCode, Intent data) {
		long fileSize = 0;
		try {
			String file_path = mCameraPhotoPath.replace("file:", "");
			File file = new File(file_path);
			fileSize = file.length();
		} catch (Exception e) {
			e.printStackTrace();
		}

		if (data != null || mCameraPhotoPath != null) {
			Integer count = 1;
			ClipData images = null;
			try {
				images = data.getClipData();
			} catch (Exception e) {
				e.printStackTrace();
			}

			if (images == null && data != null && data.getDataString() != null) {
				count = data.getDataString().length();
			} else if (images != null) {
				count = images.getItemCount();
			}
			Uri[] results = new Uri[count];
			// Check that the response is a good one
			if (resultCode == Activity.RESULT_OK) {
				Log.d(TAG, "fileSize=" + fileSize);
				if (fileSize != 0) {
					// If there is not data, then we may have taken a photo
					if (mCameraPhotoPath != null) {
						results = new Uri[] { Uri.parse(mCameraPhotoPath) };
					}
				} else if (data.getClipData() == null) {
					results = new Uri[] { Uri.parse(data.getDataString()) };
				} else {
					for (int i = 0; i < images.getItemCount(); i++) {
						results[i] = images.getItemAt(i).getUri();
					}
				}
			}
			// 区分不同系统分别返回上传结果
			if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
				mUploadMessageLollipop.onReceiveValue(results);
				mUploadMessageLollipop = null;
			} else {
				mUploadMessage.onReceiveValue(results[0]);
				mUploadMessage = null;
			}
		}
	}

其后还要注意,用户打开相册或者打开相机的时候,也有可能什么都不做就返回到原页面,由于这个取消选择的操作没有走完全流程,导致h5网页的回调资源没有回收,用户再去上传图片之时会发现页面不会响应了,因此开发者要在代码中手工替h5页面回收回调资源,这样下次用户才能继续上传图片。手工回收资源的办法是重写Activity的onResume函数,具体实现代码见下:

	@Override
	protected void onResume() {
		super.onResume();
		// 取消选择时需要回调onReceiveValue,否则网页会挂住,不会再响应点击事件
		if (mResultCode == Activity.RESULT_CANCELED) {
			try {
				if (mUploadMessageLollipop != null) {
					mUploadMessageLollipop.onReceiveValue(null);
				}
				if (mUploadMessage != null) {
					mUploadMessage.onReceiveValue(null);
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

接着即可打开实际的h5页面进行图片上传测试啦,这里的h5测试网址用的是http://m.54php.cn/demo/h5_upload,测试的调用代码很简单,设置好WebView的访问地址以及浏览器对象就好了,例子代码如下所示:

		WebView webView = (WebView) findViewById(R.id.webView);
		WebSettings webSettings = webView.getSettings();
		webSettings.setJavaScriptEnabled(true);
		webSettings.setBuiltInZoomControls(true);
		webView.loadUrl("http://m.54php.cn/demo/h5_upload");
		webView.setWebViewClient(new MyWebViewClient(this));
		webView.setWebChromeClient(new MyWebChromeClient());

最后观察一下WebView配合上述测试网址的运行界面,先看看Android4.4手机的测试画面,下面的左图为打开测试网址的初始界面,右图为点击上传按钮后在屏幕中央弹出选择对话框:

先在对话框中选择从相册上传,成功上传图片后的h5页面如下面的左图所示;重新点击上传按钮,这次选择使用相机拍照,并把照片成功上传后的h5页面如下面的右图所示:

再来看看Android6.0手机的测试画面,下面的左图为打开测试网址的初始界面,右图为点击上传按钮后在屏幕下方弹出选择对话框:

先在对话框中选择从相册上传,成功上传图片后的h5页面如下面的左图所示;重新点击上传按钮,这次选择使用相机拍照,并把照片成功上传后的h5页面如下面的右图所示:

点此查看Android开发笔记的完整目录

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券