WebView中拍照或從相簿上傳圖片
WebView 上傳圖片, 想必很多人都碰到過這樣的場景. 而且 WebView 在4.4前後的區別非常大, 比如對URL跳轉的格式, 對JS的注入宣告等等, 4.4以後的WebView 已經是chromium核心, 有多強大就無需我贅述. 說這些, 其實也是為了說明也因為WebView的前後變化太大了, 所以在低版本和版本上, WebView上傳檔案的方式都略有不同, 而且在安卓4.4.4的一些裝置上難以保證所有機型都成功.
實現過程: 在4.4之前, WebView的webkit中支援openFileChooser. 當WebView載入一個HTML頁面, 點選按鈕需要模擬form提交的方式去上傳檔案時, 就會回撥
openFileChooser(...)
方法. 然後在這個方法裡接收並處理引數ValueCallback <uri> uploadMsg. 裡面的 uri 就是所從本地選擇的檔案路徑.
然後通過Intent的startActivityForResult(…) 方法跳轉到系統選擇檔案的介面進行檔案選擇, 最後在
onActivityResult(int requestCode, int resultCode, Intent data)
方法中獲取 data中的字串路徑, 並轉換成Uri格式, 並傳給uploadMsg 即可, 類似:
String sourcePath = ImageUtil.retrievePath(this, mSourceIntent, data); if (TextUtils.isEmpty(sourcePath) || !new File(sourcePath).exists()) { Log.e(TAG, "sourcePath empty or not exists."); break; } Uri uri = Uri.fromFile(new File(sourcePath)); mUploadMsg.onReceiveValue(uri);
這樣, 接下來的上傳工作, WebView會自動完成. 當然, 這是順利的流程, 如果 onActivityResult(…) 中返回的 resultcode 不等於 Activity.RESULT_OK , 也要做一點處理, 不然再去點選第二次上傳檔案時是沒有反應的. 類似這樣:
if (resultCode != Activity.RESULT_OK) { if (mUploadMsg != null) { mUploadMsg.onReceiveValue(null); } return; }
傳個 null 即可.
OK, 上面就是4.4 以前的實現過程, 上面提及的程式碼在下載的demo裡會有, 為保證篇幅不會放在文章裡. 那麼5.0及以上的sdk變動時, webkit不再支援
openFileChooser(...)
而是提供了一個代替的方法:
public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) { return false; }
這個方法的引數跟 openFileChooser(…) 方法很像, 其實作用都是類似的, 只是 從 ValueCallback <uri> uploadMsg 變成了 ValueCallback<Uri[]> filePathCallback, 還多了FileChooserParams型別引數. fileChooserParams 其實是一個屬性封裝的引數, 裡面包含了acceptType, title等這樣的檔案屬性, 名稱等資訊.
所以, onShowFileChooser(…) 的使用方法跟 openFileChooser(…) 是很類似的, 這裡通過 onActivityResult(…) 看兩者的使用區別:
@Override public void onActivityResult(int requestCode, int resultCode, Intent data) { if (resultCode != Activity.RESULT_OK) { if (mUploadMsg != null) { mUploadMsg.onReceiveValue(null); } if (mUploadMsgForAndroid5 != null) { // for android 5.0+ mUploadMsgForAndroid5.onReceiveValue(null); } return; } switch (requestCode) { case REQUEST_CODE_IMAGE_CAPTURE: case REQUEST_CODE_PICK_IMAGE: { try { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { if (mUploadMsg == null) { return; } String sourcePath = ImageUtil.retrievePath(this, mSourceIntent, data); if (TextUtils.isEmpty(sourcePath) || !new File(sourcePath).exists()) { Log.e(TAG, "sourcePath empty or not exists."); break; } Uri uri = Uri.fromFile(new File(sourcePath)); mUploadMsg.onReceiveValue(uri); } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { if (mUploadMsgForAndroid5 == null) { // for android 5.0+ return; } String sourcePath = ImageUtil.retrievePath(this, mSourceIntent, data); if (TextUtils.isEmpty(sourcePath) || !new File(sourcePath).exists()) { Log.e(TAG, "sourcePath empty or not exists."); break; } Uri uri = Uri.fromFile(new File(sourcePath)); mUploadMsgForAndroid5.onReceiveValue(new Uri[]{uri}); } } catch (Exception e) { e.printStackTrace(); } break; } } }
主要是針對 5.0前後的系統做了一些區別處理, 其他無大異.
測試說明 這裡要特別說明的是, 經過我測試9部安卓手機, 從4.2 到6.0.1, 有8部都是正常使用. 其中包含 一部Android 4.4.2系統的手機 和 兩部Android 4.4.4的手機, 發現在魅藍note(4.4.4)上面是無法使用的, 因為4.4 Kitkat中webview是一個有點奇葩的存在, 問題這裡不贅述, 有興趣可以自行了解.
提高相容性的解決方案: 1. 使用加強版的WebView, cordova , 可以考慮編譯這個專案獲得jar包, 然後匯入專案中使用它的WebView元件. 2. 也可以通過JS呼叫本地的方法自行實現上傳.
以上兩個方法的相容性都相當不錯, 可自行嘗試.
可能導致失敗的原因 如果選擇檔案到上傳檔案的過程中失敗, 有可能是以下原因導致的: 1. 檔案的路徑包含中文. (9部裝置中, vivo X6D不支援中文路徑包含中文) 2. 手機系統是Android 6.0以上 (API >= 23), 且沒有獲得檔案和攝像頭許可權.
如果你的專案中還相容到4.0以下的版本, build.gradle 配置檔案中的compileSdkVersion 和 targetSdkVersion 是16甚至更低, 那麼恭喜你, 直接使用
openFileChooser(...)
這種處理方法即可.測試裝置列表 1. 中移動 CM601 (4.2.2) 2. 華為榮耀6 H60-L01 (4.4.2) 3. 華為榮耀暢玩4X Che1-CL20 (4.4.4) 4. 魅藍note (4.4.4) 5. 聯想K50-t5 (5.0) 6. 魅藍note3 (5.1) 7. vivo X6D (5.1) 8. 小米note (6.0.1) 9. 華為mate8 (6.0.1) 10. 三星Galaxy S6 SM-G9200 (6.0.1)
下面附上測試部分測試手機圖, 由於拿了真實專案的地址做測試, 這裡做了一些可愛的馬賽克處理 請諒解諒解~
這篇部落格寫的很不錯了,但是6.0以上的手機要自己做處理,比如許可權適配啊,7.0的還要處理uri,伸手黨出門右拐,不送!