1. 程式人生 > >Android開發筆記(一百五十二)H5通過WebView上傳圖片

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開發筆記的完整目錄