一乌助、引言
webview怎么實(shí)現(xiàn)web的<input type="file" />選擇圖片功能萌京,如何讓h5通過webview調(diào)用系統(tǒng)相冊(cè)和相機(jī)焰轻,并在圖片傳回h5的時(shí)已經(jīng)將圖片做了壓縮處理数苫?本篇就是解決這方面的問題聪舒。
這邊h5 的<input type="file" />上傳文件只是限定于圖片類型,不需要pdf虐急、txt等其他類型箱残,如果要寫一個(gè)普通的不限定于圖片的上傳文件功能可以參考http://www.reibang.com/p/b0f1fdbfd502。
二止吁、Webview實(shí)現(xiàn)
Webview要調(diào)用系統(tǒng)相冊(cè)/相機(jī)被辑,需要setWebChromeClient并重寫WebChromeClient的方法。
mWebView.setWebChromeClient(new WebChromeClient(){
// For Android < 3.0
public void openFileChooser(ValueCallback<Uri> valueCallback) {
mUploadCallBack = valueCallback;
showFileChooser();
}
// For Android >= 3.0
public void openFileChooser(ValueCallback valueCallback, String acceptType) {
mUploadCallBack = valueCallback;
showFileChooser();
}
//For Android >= 4.1
public void openFileChooser(ValueCallback<Uri> valueCallback, String acceptType, String capture) {
mUploadCallBack = valueCallback;
showFileChooser();
}
// For Android >= 5.0
@Override
public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, WebChromeClient.FileChooserParams fileChooserParams) {
mUploadCallBackAboveL = filePathCallback;
showFileChooser();
return true;
}
});
/**
* 打開選擇圖片/相機(jī)
*/
private void showFileChooser() {
Intent intent1 = new Intent(Intent.ACTION_PICK, null);
intent1.setDataAndType(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*");
// Intent intent1 = new Intent(Intent.ACTION_GET_CONTENT);
// intent1.addCategory(Intent.CATEGORY_OPENABLE);
// intent1.setType("image/*");
Intent intent2 = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
mCameraFilePath = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator +
System.currentTimeMillis() + ".jpg";
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
// android7.0注意uri的獲取方式改變
Uri photoOutputUri = FileProvider.getUriForFile(
MainActivity.this,
BuildConfig.APPLICATION_ID + ".fileProvider",
new File(mCameraFilePath));
intent2.putExtra(MediaStore.EXTRA_OUTPUT, photoOutputUri);
} else {
intent2.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File(mCameraFilePath)));
}
Intent chooser = new Intent(Intent.ACTION_CHOOSER);
chooser.putExtra(Intent.EXTRA_TITLE, "File Chooser");
chooser.putExtra(Intent.EXTRA_INTENT,intent1);
chooser.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Intent[]{intent2});
startActivityForResult(chooser, REQUEST_CODE_FILE_CHOOSER);
}
然后敬惦,在onActivityResult里處理獲取到的圖片盼理。
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE_FILE_CHOOSER) {
Uri result = data == null || resultCode != RESULT_OK ? null : data.getData();
// 壓縮到多少寬度以內(nèi)
int maxW = 1000;
// 壓縮到多少大小以內(nèi),1024kb
int maxSize = 1024;
if (result == null) {
// 看是否從相機(jī)返回
File cameraFile = new File(mCameraFilePath);
if (cameraFile.exists()) {
result = Uri.fromFile(cameraFile);
sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, result));
}
}
if (result != null) {
// 根據(jù)uri獲取路徑
String path = FileUtils.getPath(this, result);
if (!TextUtils.isEmpty(path)) {
File f = new File(path);
if (f.exists() && f.isFile()) {
// 按大小和尺寸壓縮圖片
Bitmap b = getCompressBitmap(path, maxW, maxW, maxSize);
String basePath = Environment.getExternalStorageDirectory().getAbsolutePath();
String compressPath = basePath + File.separator + "photos" + File.separator
+ System.currentTimeMillis() + ".jpg";
// 壓縮完保存在文件里
if (saveBitmapToFile(b, compressPath)) {
Uri newUri = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
newUri = FileProvider.getUriForFile(
MainActivity.this,
BuildConfig.APPLICATION_ID + ".fileProvider",
new File(compressPath));
} else {
newUri = Uri.fromFile(new File(compressPath));
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
if (mUploadCallBackAboveL != null) {
if (newUri != null) {
mUploadCallBackAboveL.onReceiveValue(new Uri[]{newUri});
mUploadCallBackAboveL = null;
return;
}
}
} else if (mUploadCallBack != null) {
if (newUri != null) {
mUploadCallBack.onReceiveValue(newUri);
mUploadCallBack = null;
return;
}
}
}
}
}
}
clearUploadMessage();
return;
}
}
如果沒有圖片返回給h5,記得要執(zhí)行下面代碼俄删,避免h5下次點(diǎn)擊選擇圖片時(shí)無(wú)法響應(yīng)宏怔。
/**
* webview沒有選擇圖片也要傳null,防止下次無(wú)法執(zhí)行
*/
private void clearUploadMessage(){
if (mUploadCallBackAboveL != null) {
mUploadCallBackAboveL.onReceiveValue(null);
mUploadCallBackAboveL = null;
}
if (mUploadCallBack != null) {
mUploadCallBack.onReceiveValue(null);
mUploadCallBack = null;
}
}
相關(guān)的壓縮和保存圖片代碼如下:
/**
* 根據(jù)路徑獲取bitmap(壓縮后)
*
* @param srcPath 圖片路徑
* @param width 最大寬(壓縮完可能會(huì)大于這個(gè)畴椰,這邊只是作為大概限制举哟,避免內(nèi)存溢出)
* @param height 最大高(壓縮完可能會(huì)大于這個(gè),這邊只是作為大概限制迅矛,避免內(nèi)存溢出)
* @param size 圖片大小妨猩,單位kb
* @return 返回壓縮后的bitmap
*/
public static Bitmap getCompressBitmap(String srcPath, float width, float height, int size) {
BitmapFactory.Options newOpts = new BitmapFactory.Options();
// 開始讀入圖片,此時(shí)把options.inJustDecodeBounds 設(shè)回true了
newOpts.inJustDecodeBounds = true;
BitmapFactory.decodeFile(srcPath, newOpts);
newOpts.inJustDecodeBounds = false;
int w = newOpts.outWidth;
int h = newOpts.outHeight;
int scaleW = (int) (w / width);
int scaleH = (int) (h / height);
int scale = scaleW < scaleH ? scaleH : scaleW;
if (scale <= 1) {
scale = 1;
}
newOpts.inSampleSize = scale;// 設(shè)置縮放比例
// 重新讀入圖片秽褒,注意此時(shí)已經(jīng)把options.inJustDecodeBounds 設(shè)回false了
Bitmap bitmap = BitmapFactory.decodeFile(srcPath, newOpts);
// 壓縮好比例大小后再進(jìn)行質(zhì)量壓縮
return compressImage(bitmap, size);
}
/**
* 圖片質(zhì)量壓縮
*
* @param image 傳入的bitmap
* @param size 壓縮到多大壶硅,單位kb
* @return 返回壓縮完的bitmap
*/
public static Bitmap compressImage(Bitmap image, int size) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
// 質(zhì)量壓縮方法,這里100表示不壓縮销斟,把壓縮后的數(shù)據(jù)存放到baos中
int options = 100;
image.compress(Bitmap.CompressFormat.JPEG, options, baos);
// 循環(huán)判斷如果壓縮后圖片是否大于size,大于繼續(xù)壓縮
while (baos.toByteArray().length / 1024 > size) {
// 重置baos即清空baos
baos.reset();
// 每次都減少10
options -= 10;
// 這里壓縮options%庐椒,把壓縮后的數(shù)據(jù)存放到baos中
image.compress(Bitmap.CompressFormat.JPEG, options, baos);
}
// 把壓縮后的數(shù)據(jù)baos存放到ByteArrayInputStream中
ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());
// 把ByteArrayInputStream數(shù)據(jù)生成圖片
Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, null);
return bitmap;
}
/**
* bitmap保存為文件
*
* @param bm bitmap
* @param filePath 文件路徑
* @return 返回保存結(jié)果 true:成功,false:失敗
*/
public static boolean saveBitmapToFile(Bitmap bm, String filePath) {
try {
File file = new File(filePath);
file.deleteOnExit();
if (!file.getParentFile().exists()) {
file.getParentFile().mkdirs();
}
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file));
boolean b = false;
if (filePath.toLowerCase().endsWith(".png")) {
b = bm.compress(Bitmap.CompressFormat.PNG, 100, bos);
} else {
b = bm.compress(Bitmap.CompressFormat.JPEG, 100, bos);
}
bos.flush();
bos.close();
return b;
} catch (FileNotFoundException e) {
} catch (IOException e) {
}
return false;
}
對(duì)于根據(jù)uri獲取圖片路徑的代碼也比較關(guān)鍵蚂踊。相關(guān)代碼可以參考http://www.reibang.com/p/25c35da68db2
如果你的應(yīng)用混淆了要注意下,openFileChooser方法并不是WebChromeClient的對(duì)外開放的方法约谈,因此這個(gè)方法會(huì)被混淆,解決辦法也比較簡(jiǎn)單,只需要在混淆文件里控制一下即可:
-keepclassmembers class * extends android.webkit.WebChromeClient{
public void openFileChooser(...);
}
好了棱诱,這樣就可以讓webview調(diào)用原生相機(jī)和相冊(cè)選擇圖片泼橘,并對(duì)圖片做壓縮處理。