真正可用的安卓webview html图片上传限制突破处理(拍照+相册都可以用)
两篇起步使用webview参考文章,第一篇解除限制,但会调用外部浏览器打开链接,第二篇 覆盖shouldOverrideUrlLoading return true
https://www.jb51.net/article/104199.htm
https://www.cnblogs.com/sohowang/p/3998155.html
android 多媒体和相机详解五(先学习下相机和相册的基本操作)
https://www.2cto.com/kf/201207/143320.html
有拍照和选相册(拍照后无法上传,相册选择可以)
https://blog.csdn.net/a_running_wolf/article/details/77983739
https://download.csdn.net/download/st666/9621904?locationNum=4
真正可用的拍照上传例子在这(可正常拍照,但没有选择相册)
https://download.csdn.net/download/wangchsh2008/10232317
但是上面这些例子都没有图片压缩功能,上传拍照的照片过大(>2M)通过流量的会失败,看来还是有一定的实用限制
H5异步上传前台(支持选择后即时查看图片)
<input type="file" name="thefile" id="j-file"> <img src="" id='j-img' alt="" width="100%"> <button id='j-btn'>upload</button> <script type="text/javascript"> var o_file = document.getElementById('j-file'), o_btn = document.getElementById('j-btn'), o_img = document.getElementById('j-img'), target_file = null; o_file.addEventListener('change',function(event){ var file = event.target.files[0]; if(!file) return; target_file = file; var url = window.URL.createObjectURL(target_file); if(/image/.test(target_file.type)){ o_img.setAttribute('src',url); }else{ console.log('请选择图片'); } },false); o_btn.onclick = function(){ if(!target_file) return; //数据处理 var data = new FormData(); data.append('thefile',target_file); var xhr = new XMLHttpRequest(); if(xhr.upload){ xhr.upload.addEventListener("progress", function(e){ var loaded = e.loaded; //已经上传大小情况 var tot = e.total; //附件总大小 var per = Math.floor(100*loaded/tot); //已经上传的百分比 console.log(per+'%');//进度 }, false); } xhr.onreadystatechange = function(e) { if (xhr.readyState == 4) { if (xhr.status >=200&&xhr.status<300||xhr.status==304) { //上传成功 } } }; xhr.onloadend = function(){ //无论失败或成功 } xhr.onerror = function(){ //网络失败 } // 开始上传 xhr.open("POST",'/LBM/Home/SspBuzPicMng/uploadFile', true); xhr.send(data); } </script>
安卓代码(拍照+相册选择都可以哦,流量上传超过2M容易失败)
package com.gtj.admin.gapt; import android.Manifest; import android.annotation.TargetApi; import android.app.Activity; import android.content.ClipData; import android.content.ContentValues; import android.content.Intent; import android.content.pm.PackageManager; import android.graphics.Bitmap; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.Environment; import android.os.Parcelable; import android.os.StrictMode; import android.provider.MediaStore; import android.support.annotation.RequiresApi; import android.support.v4.app.ActivityCompat; import android.text.format.DateFormat; import android.util.Log; import android.webkit.ValueCallback; import android.webkit.WebChromeClient; import android.webkit.WebResourceRequest; import android.webkit.WebSettings; import android.webkit.WebView; import android.webkit.WebViewClient; import android.widget.Toast; import java.io.File; import java.io.IOException; import java.util.Calendar; import java.util.Locale; public class MainActivity extends Activity { private static final String TAG = MainActivity.class.getSimpleName(); private WebView mWebView; private String TMP_URL = "http://221.2.169.102:50001/LBM"; private ValueCallback<Uri> mUploadMessage;// 表单的数据信息 private ValueCallback<Uri[]> mUploadCallbackAboveL; private final static int FILECHOOSER_RESULTCODE = 1;// 表单的结果回调</span> private Uri imageUri; File file; @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN) @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mWebView = (WebView) findViewById(R.id.main_web); mWebView.loadUrl(TMP_URL); mWebView.getSettings().setJavaScriptEnabled(true); //mWebView.getSettings().setRenderPriority(WebSettings.RenderPriority.HIGH); WebSettings settings = mWebView.getSettings(); settings.setDomStorageEnabled(true); settings.setUseWideViewPort(true); settings.setLoadWithOverviewMode(true); settings.setAllowContentAccess(true); // 是否可访问Content Provider的资源,默认值 true settings.setAllowFileAccess(true); // 是否可访问本地文件,默认值 true // 是否允许通过file url加载的Javascript读取本地文件,默认值 false settings.setAllowFileAccessFromFileURLs(false); // 是否允许通过file url加载的Javascript读取全部资源(包括文件,http,https),默认值 false settings.setAllowUniversalAccessFromFileURLs(false); settings.setJavaScriptEnabled(true); settings.setSupportZoom(true); //检查权限 checkAppPermission(); mWebView.setWebViewClient(new WebViewClient() { @TargetApi(Build.VERSION_CODES.LOLLIPOP) @Override public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) { //返回false,意味着请求过程里,不管有多少次的跳转请求(即新的请求地址),均交给webView自己处理,这也是此方法的默认处理 //返回true,说明你自己想根据url,做新的跳转,比如在判断url符合条件的情况下,我想让webView加载http://ask.csdn.net/questions/178242 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { if (request.getUrl().toString().contains("sina.cn")){ view.loadUrl("http://ask.csdn.net/questions/178242"); return true; } } view.loadUrl(request.getUrl().toString()); return true; } @Override public void onPageStarted(WebView view, String url, Bitmap favicon) { // TODO Auto-generated method stub super.onPageStarted(view, url, favicon); } @Override public void onPageFinished(WebView view, String url) { // TODO Auto-generated method stub super.onPageFinished(view, url); } }); mWebView.setWebChromeClient(new WebChromeClient() { @Override // onShowFileChooser详解 http://teachcourse.cn/2224.html public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) { mUploadCallbackAboveL = filePathCallback; take(); return true; } public void openFileChooser(ValueCallback<Uri> uploadMsg) { mUploadMessage = uploadMsg; take(); } public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType) { mUploadMessage = uploadMsg; take(); } public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) { mUploadMessage = uploadMsg; take(); } }); //android 7.0系统解决拍照的问题 // StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder(); // StrictMode.setVmPolicy(builder.build()); // builder.detectFileUriExposure(); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == FILECHOOSER_RESULTCODE) { updatePhotos(); if (null == mUploadMessage && null == mUploadCallbackAboveL) return; Uri result = data == null || resultCode != RESULT_OK ? null : data.getData(); if (mUploadCallbackAboveL != null) { onActivityResultAboveL(requestCode, resultCode, data); } else if (mUploadMessage != null) { Log.e("result", result + ""); if (result == null) { // mUploadMessage.onReceiveValue(imageUri); mUploadMessage.onReceiveValue(imageUri); mUploadMessage = null; Log.e("imageUri", imageUri + ""); } else { mUploadMessage.onReceiveValue(result); mUploadMessage = null; } } } } @SuppressWarnings("null") @TargetApi(Build.VERSION_CODES.LOLLIPOP) private void onActivityResultAboveL(int requestCode, int resultCode, Intent data) { if (requestCode != FILECHOOSER_RESULTCODE || mUploadCallbackAboveL == null) { return; } Uri[] results = null; if (resultCode == Activity.RESULT_OK) { if (data == null) { results = new Uri[]{imageUri}; } else { String dataString = data.getDataString(); ClipData clipData = data.getClipData(); if (clipData != null) { results = new Uri[clipData.getItemCount()]; for (int i = 0; i < clipData.getItemCount(); i++) { ClipData.Item item = clipData.getItemAt(i); results[i] = item.getUri(); } } if (dataString != null) results = new Uri[]{Uri.parse(dataString)}; } } if (results != null) { mUploadCallbackAboveL.onReceiveValue(results); mUploadCallbackAboveL = null; } else { results = new Uri[]{imageUri}; mUploadCallbackAboveL.onReceiveValue(results); mUploadCallbackAboveL = null; } return; } private void take() { // int hasCameraContactsPermission = ActivityCompat.checkSelfPermission(this,Manifest.permission.CAMERA); // if (hasCameraContactsPermission != PackageManager.PERMISSION_GRANTED) { // String[] PERMISSIONS_STORAGE = { // Manifest.permission.CAMERA}; // // ActivityCompat.requestPermissions(this, PERMISSIONS_STORAGE, // 1); // } // // int hasWriteContactsPermission = ActivityCompat.checkSelfPermission(this,Manifest.permission.WRITE_EXTERNAL_STORAGE); // if (hasWriteContactsPermission != PackageManager.PERMISSION_GRANTED) { // String[] PERMISSIONS_STORAGE = { // Manifest.permission.WRITE_EXTERNAL_STORAGE}; // // ActivityCompat.requestPermissions(this, PERMISSIONS_STORAGE, // 1); // } Log.i(TAG,""+Environment.getExternalStorageDirectory().getPath()); // 指定拍照存储位置的方式调起相机 String filePath = Environment.getExternalStorageDirectory() + File.separator + Environment.DIRECTORY_PICTURES + File.separator; String fileName = "IMG_" + DateFormat.format("yyyyMMdd_hhmmss", Calendar.getInstance(Locale.CHINA)) + ".jpg"; file = new File(filePath + fileName); imageUri = Uri.fromFile(file); //调用照相机和浏览图片库代码 // Intent captureIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE); // captureIntent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri); // // Intent Photo = new Intent(Intent.ACTION_PICK, // android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI); // // Intent chooserIntent = Intent.createChooser(Photo, "Image Chooser"); // chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Parcelable[]{captureIntent}); // startActivityForResult(chooserIntent, FILECHOOSER_RESULTCODE); //调用系统相机,此代码在android 7.0以上有问题,需要在onCreate方法加入StrictMode.VmPolicy.Builder解决办法 // Intent intentCamera = new Intent(); // if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { // intentCamera.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); //添加这一句表示对目标应用临时授权该Uri所代表的文件 // } // intentCamera.setAction(MediaStore.ACTION_IMAGE_CAPTURE); // //将拍照结果保存至photo_file的Uri中,不保留在相册中 // intentCamera.putExtra(MediaStore.EXTRA_OUTPUT, imageUri); // startActivityForResult(intentCamera, FILECHOOSER_RESULTCODE); //Intent captureIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE); //captureIntent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri); //兼容android 7.0+版本的照相机调用代码 Intent captureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); if (captureIntent.resolveActivity(getPackageManager()) != null) { /*获取当前系统的android版本号*/ int currentapiVersion = android.os.Build.VERSION.SDK_INT; Log.e("currentapiVersion","currentapiVersion====>"+currentapiVersion); if (currentapiVersion<24){ captureIntent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri); //startActivityForResult(intent, FILECHOOSER_RESULTCODE); }else { ContentValues contentValues = new ContentValues(1); contentValues.put(MediaStore.Images.Media.DATA, file.getAbsolutePath()); Uri uri = this.getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,contentValues); captureIntent.putExtra(MediaStore.EXTRA_OUTPUT, uri); //startActivityForResult(intent, FILECHOOSER_RESULTCODE); } } else { Toast.makeText(this, "照相机不存在", Toast.LENGTH_SHORT).show(); return; } Intent Photo = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI); Intent chooserIntent = Intent.createChooser(Photo, "Image Chooser"); chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Parcelable[]{captureIntent}); startActivityForResult(chooserIntent, FILECHOOSER_RESULTCODE); } private void updatePhotos() { // 该广播即使多发(即选取照片成功时也发送)也没有关系,只是唤醒系统刷新媒体文件 Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE); intent.setData(imageUri); sendBroadcast(intent); } //这里检查了存储权限,如果有需要,可以先检查下相机权限。 此方法用户对权限禁止或允许 未做判断,需要进一步完善 private void checkAppPermission(){ int permission = ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE); if (permission != PackageManager.PERMISSION_GRANTED) { String[] PERMISSIONS_STORAGE = { Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE}; final int REQUEST_EXTERNAL_STORAGE = 1; ActivityCompat.requestPermissions(this, PERMISSIONS_STORAGE, REQUEST_EXTERNAL_STORAGE); } } }
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.gtj.admin.gapt" > <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme" > <activity android:name=".MainActivity" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity><!-- ATTENTION: This was auto-generated to add Google Play services to your project for App Indexing. See https://g.co/AppIndexing/AndroidStudio for more information. --> <meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version" /> </application> </manifest>