前提:需要首先实现web端的网页上传图片(只要网页上传图片成功,后续集成和android部分的相关操作,就可以成功实现图片上传)
步骤:
- web端实现网页上传
- web嵌入安卓webview实现安卓手机拍照和选择图片上传
问题:
- 对安卓开发不太了解的web开发小伙伴,需要粗略的了解一下安卓开发的一些基本内容,这一步需要自己去适当的学习,如果这一步没有,那就不用往下看了
- 图片上传和拍照上传,需要的一些权限
- 高安卓版本的手机拍照上传,需要进行的额外操作
web端实现网页上传的代码这边就不赘述了,需要了解的,可以联系我vx: 18918813817
接下来就是安卓部分的相关代码
// 拍照 + 文件选择
private android.webkit.ValueCallback<Uri[]> mUploadCallbackAboveL;
private Uri imageUri;
private int REQUEST_CODE = 1234;
// 需要实现图片上传,需要重载onShowFileChooser方法
private WebChromeClient webChromeClient = new WebChromeClient() {
@Override
public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
mUploadCallbackAboveL = filePathCallback;
try {
takePhoto();
} catch (Exception e) {
Log.e("JKT", e.toString());
}
return true;
}
};
这里是重载了onShowFileChooser方法,低版本的android是重载另外一个方法(低版本好像是<=4.0,个人觉得版本太低,可能已经没有什么需求是要适应这种版本的手机了,如果你需要了解低版本,自行百度吧)
好,下面开始takePhoto这个函数的重点部分
/**
* 调用相机
*/
private void takePhoto() {
// 指定拍照存储位置的方式调起相机
String filePath = Environment.getExternalStorageDirectory() + File.separator
+ Environment.DIRECTORY_PICTURES + File.separator;
String fileName = "IMG_" + DateFormat.format("yyyyMMdd_hhmmss", Calendar.getInstance(Locale.CHINA)) + ".jpg";
File fileUri = new File(filePath + fileName);
imageUri = Uri.fromFile(fileUri);
// 这里针对高版本的android需要进行特殊的图片路径设置,重点1
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
imageUri = FileProvider.getUriForFile(MainActivity.this, BuildConfig.APPLICATION_ID + ".provider", fileUri);
}
Intent captureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
captureIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
captureIntent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
Intent Photo = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
Intent chooserIntent = Intent.createChooser(Photo, "Image Chooser");
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Parcelable[]{captureIntent});
startActivityForResult(chooserIntent, REQUEST_CODE);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE) {
if (resultCode == 0) {
// 这里需要对相机,和读取文件权限做检查,没有权限需要授权,重点2
this.checkCameraPermissions();
}
if (mUploadCallbackAboveL != null) {
chooseAbove(resultCode, data);
} else {
Toast.makeText(this, "发生错误", Toast.LENGTH_SHORT).show();
}
}
}
/**
* Android API >= 21(Android 5.0) 版本的回调处理
* @param resultCode 选取文件或拍照的返回码
* @param data 选取文件或拍照的返回结果
*/
// 重点3: 获取android的activity回调(也就是文件的数据),对拍照上传和选择图片上传分开处理
private void chooseAbove(int resultCode, Intent data) {
Log.e("JKT", "返回调用方法--chooseAbove");
if (RESULT_OK == resultCode) {
updatePhotos();
if (data != null) {
// 这里是针对从文件中选图片的处理
Uri[] results;
Uri uriData = data.getData();
if (uriData != null) {
results = new Uri[]{uriData};
for (Uri uri : results) {
Log.e("JKT", "系统返回URI:" + uri.toString());
}
mUploadCallbackAboveL.onReceiveValue(results);
} else {
mUploadCallbackAboveL.onReceiveValue(null);
}
} else {
Log.e("JKT", "自定义结果:" + imageUri.toString());
mUploadCallbackAboveL.onReceiveValue(new Uri[]{imageUri});
}
} else {
mUploadCallbackAboveL.onReceiveValue(null);
}
mUploadCallbackAboveL = null;
}
private void updatePhotos() {
// 该广播即使多发(即选取照片成功时也发送)也没有关系,只是唤醒系统刷新媒体文件
Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
intent.setData(imageUri);
sendBroadcast(intent);
}
private void checkCameraPermissions () {
List<String> permissiongList = new ArrayList<>();
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
permissiongList.add(Manifest.permission.CAMERA);
}
if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
permissiongList.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
}
if (!permissiongList.isEmpty()) {
String[] permissions = permissiongList.toArray(new String[permissiongList.size()]);
ActivityCompat.requestPermissions(this, permissions, 1);
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
case 1:
if (grantResults.length > 0){
for (int result : grantResults) {
if (result != PackageManager.PERMISSION_GRANTED) {
Toast.makeText(getApplicationContext(), "需用户同意使用相关权限,才能正常使用该功能", Toast.LENGTH_SHORT).show();
return;
}
}
} else {
Toast.makeText(getApplicationContext(), "系统发生未知错误,即将退出", Toast.LENGTH_SHORT).show();
finish();
}
break;
default:
break;
}
}
到了这里,应该上传图片,和拍照上传图片的问题应该可以解决了,但是当你使用高版本android手机进行拍照上传的时候,发现没有上传成功
通过日志发现,未能获取到读取文件相关权限吧,这是因为android高版本的手机对文件读写进行了额外的操作吧(<!--在安卓7.0以上禁止在应用外部公开 file://URI-->),所以需要我们多一些设置
在AndroidManifest.xml中添加provider
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider"
android:grantUriPermissions="true"
android:exported="false">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_path" />
</provider>
在res目录下新建xml文件夹,新建文件file_path.xml
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path
name="external_files"
path="." />
</paths>
OK,拍照上传和文件上传的具体实现就大概这么多吧,遇到了一些问题,主要是权限相关的,还有安卓高版本文件读写的问题
因为是一枚web开发人员,android开发经验不多,了解的也不够透彻,所以如果有问题,希望看友可以指出来,非常感谢!