android开发分享Android 拍照选择图片并上传功能的实现思路(包含权限动态获取)

作为一个android新手,想实现手机拍照并上传的功能,经过查找资料,已实现此功能。在此记录备忘。老鸟请忽略。 一、实现思路: 1.android手机客户端,拍照(或选择图片),然

作为一个android新手,想实现手机拍照并上传的功能,经过查找资料,已实现此功能。在此记录备忘。老鸟请忽略。

一、实现思路:

1.android手机客户端,拍照(或选择图片),然后上传到服务器。

2.服务器端接收手机端上传上来的图片。

二、实现步骤:

1.按惯例,先放效果图:

Android 拍照选择图片并上传功能的实现思路(包含权限动态获取)

项目结构:

Android 拍照选择图片并上传功能的实现思路(包含权限动态获取)

2.activity_main.xml

  <?xml version="1.0" encoding="utf-8"?>  <linearlayout xmlns:android="https://schemas.android.com/apk/res/android"   android:layout_width="match_parent"   android:layout_height="match_parent"   android:orientation="vertical"   android:padding="5dp">      <textview   android:layout_width="wrap_content"   android:layout_height="wrap_content"   android:text="图片预览" />      <imageview   android:id="@+id/imageview"   android:layout_width="match_parent"   android:layout_height="400dp"   android:background="#fff"   android:padding="1dp"   android:scaletype="fitxy" />      <linearlayout   android:layout_width="wrap_content"   android:layout_height="wrap_content"   android:layout_gravity="center_horizontal"   android:orientation="horizontal">      <button   android:id="@+id/btnphoto"   android:layout_width="wrap_content"   android:layout_height="wrap_content"   android:text="拍照" />      <button   android:id="@+id/btnselect"   android:layout_width="wrap_content"   android:layout_height="wrap_content"   android:text="选择" />   </linearlayout>  </linearlayout>

3.mainactivity.java

  package com.qingshan.note;  import androidx.annotation.nonnull;  import androidx.annotation.requiresapi;  import androidx.appcompat.app.appcompatactivity;  import androidx.core.app.activitycompat;  import androidx.core.content.contextcompat;  import android.manifest;  import android.app.alertdialog;  import android.content.contentvalues;  import android.content.dialoginterface;  import android.content.intent;  import android.content.pm.packagemanager;  import android.graphics.bitmap;  import android.graphics.bitmapfactory;  import android.graphics.matrix;  import android.net.uri;  import android.os.build;  import android.os.bundle;  import android.os.environment;  import android.provider.mediastore;  import android.provider.settings;  import android.view.view;  import android.widget.button;  import android.widget.imageview;  import android.widget.toast;  import java.io.bufferedreader;  import java.io.datainputstream;  import java.io.dataoutputstream;  import java.io.file;  import java.io.fileinputstream;  import java.io.filenotfoundexception;  import java.io.fileoutputstream;  import java.io.ioexception;  import java.io.inputstreamreader;  import java.io.outputstream;  import java.net.httpurlconnection;  import java.net.url;  import java.text.simpledateformat;  import java.util.arraylist;  import java.util.date;  import java.util.hashmap;  import java.util.iterator;  import java.util.list;  import java.util.map;  public class mainactivity extends appcompatactivity implements view.onclicklistener {   private button btnphoto, btnselect;   private intent intent;   private final int camera = 1;//事件枚举(可以自定义)   private final int choose = 2;//事件枚举(可以自定义)   private final string posturl = "https://qingshanboke.com/home/andoriduploadfile";//接收上传图片的地址   string photopath = "";//要上传的图片路径   private final int permissioncode = 100;//权限请求码   //权限集合,对应在androidmanifest.xml文件中添加配置   // <uses-permission android:name="android.permission.camera" />   // <uses-permission android:name="android.permission.write_external_storage" />   // <uses-permission android:name="android.permission.access_network_state" />   // <uses-permission android:name="android.permission.access_wifi_state"/>   // <uses-permission android:name="android.permission.internet"/>   string[] permissions = new string[]{   manifest.permission.camera,   manifest.permission.write_external_storage,   manifest.permission.access_network_state,   manifest.permission.access_wifi_state,   manifest.permission.internet   };   alertdialog alertdialog;   @override   protected void oncreate(bundle savedinstancestate) {   super.oncreate(savedinstancestate);   setcontentview(r.layout.activity_main);   //6.0才用动态权限   if (build.version.sdk_int >= 23) {   checkpermission();   }   btnphoto = findviewbyid(r.id.btnphoto);   btnselect = findviewbyid(r.id.btnselect);   btnphoto.setonclicklistener(this);   btnselect.setonclicklistener(this);   }   //检查权限   private void checkpermission() {   list<string> permissionlist = new arraylist<>();   for (int i = 0; i < permissions.length; i++) {   if (contextcompat.checkselfpermission(this, permissions[i]) != packagemanager.permission_granted) {   permissionlist.add(permissions[i]);   }   }   if (permissionlist.size() <= 0) {   //说明权限都已经通过,可以做你想做的事情去   } else {   //存在未允许的权限   activitycompat.requestpermissions(this, permissions, permissioncode);   }   }   //授权后回调函数   @override   public void onrequestpermissionsresult(int requestcode, @nonnull string[] permissions, @nonnull int[] grantresults) {   super.onrequestpermissionsresult(requestcode, permissions, grantresults);   boolean haspermission = false;   if (permissioncode == requestcode) {   for (int i = 0; i < grantresults.length; i++) {   if (grantresults[i] == -1) {   haspermission = true;   }   }   if (haspermission) {   //跳转到系统设置权限页面,或者直接关闭页面,不让他继续访问   permissiondialog();   } else {   //全部权限通过,可以进行下一步操作   }   }   }   //打开手动设置应用权限   private void permissiondialog() {   if (alertdialog == null) {   alertdialog = new alertdialog.builder(this)   .settitle("提示信息")   .setmessage("当前应用缺少必要权限,该功能暂时无法使用。如若需要,请单击【确定】按钮前往设置中心进行权限授权。")   .setpositivebutton("设置", new dialoginterface.onclicklistener() {   @override   public void onclick(dialoginterface dialog, int which) {   cancelpermissiondialog();   uri packageuri = uri.parse("package:" + getpackagename());   intent intent = new intent(settings.action_application_details_settings, packageuri);   startactivity(intent);   }   })   .setnegativebutton("取消", new dialoginterface.onclicklistener() {   @override   public void onclick(dialoginterface dialog, int which) {   cancelpermissiondialog();   }   })   .create();   }   alertdialog.show();   }   //用户取消授权   private void cancelpermissiondialog() {   alertdialog.cancel();   }   @override   public void onclick(view v) {   switch (v.getid()) {   //拍照按钮事件   case r.id.btnphoto:   //方法一:这样拍照只能取到缩略图(不清晰)   //intent = new intent(android.provider.mediastore.action_image_capture);   //startactivityforresult(intent, camera);   //方法二:指定加载路径图片路径(保存原图,清晰)   string sd_path = environment.getexternalstoragedirectory().getpath() + "/拍照上传示例/";   simpledateformat format = new simpledateformat("yyyymmddhhmmss");   string filename = format.format(new date(system.currenttimemillis())) + ".jpeg";   photopath = sd_path + filename;   file file = new file(photopath);   if (!file.getparentfile().exists()) {   file.getparentfile().mkdirs();   }   //兼容7.0以上的版本   if (build.version.sdk_int >= build.version_codes.n) {   try {   contentvalues values = new contentvalues(1);   values.put(mediastore.images.media.mime_type, "image/jpg");   values.put(mediastore.images.media.data, photopath);   uri tempuri = getcontentresolver().insert(mediastore.images.media.external_content_uri, values);   intent intent = new intent(mediastore.action_image_capture);   intent.addflags(intent.flag_grant_read_uri_permission | intent.flag_grant_write_uri_permission);   if (tempuri != null) {   intent.putextra(mediastore.extra_output, tempuri);   intent.putextra(mediastore.extra_video_quality, 1);   }   startactivityforresult(intent, camera);   } catch (exception e) {   e.printstacktrace();   }   } else {   intent = new intent(mediastore.action_image_capture);   uri uri = uri.fromfile(file);   intent.putextra(mediastore.extra_output, uri); //指定拍照后的存储路径,保存原图   startactivityforresult(intent, camera);   }   break;   //选择按钮事件   case r.id.btnselect:   intent = new intent(intent.action_pick, android.provider.mediastore.images.media.external_content_uri);   startactivityforresult(intent, choose);   break;   }   }   @requiresapi(api = build.version_codes.o)   @override   protected void onactivityresult(int requestcode, int resultcode, intent data) {   super.onactivityresult(requestcode, resultcode, data);   switch (requestcode) {   // 调用照相机拍照   case camera:   if (resultcode == result_ok) {   //对应方法一:图片未保存,需保存文件到本地  // bundle bundle = data.getextras();  // bitmap bitmap = (bitmap) bundle.get("data");  // string savepath;  // string sd_path = environment.getexternalstoragedirectory().getpath() + "/拍照上传示例/";  // simpledateformat format = new simpledateformat("yyyymmddhhmmss");  // string filename = format.format(new date(system.currenttimemillis())) + ".jpeg";  // if (environment.getexternalstoragestate().equals(environment.media_mounted)) {  // savepath = sd_path;  // } else {  // toast.maketext(mainactivity.this, "保存失败!", toast.length_short).show();  // return;  // }  // photopath = savepath + filename;  // file file = new file(photopath);  // try {  // if (!file.exists()) {  // file.getparentfile().mkdirs();  // file.createnewfile();  // }  // fileoutputstream stream = new fileoutputstream(file);  // bitmap.compress(bitmap.compressformat.jpeg, 100, stream);  // toast.maketext(mainactivity.this, "保存成功,位置:" + file.getabsolutepath(), toast.length_short).show();  // } catch (ioexception e) {  // e.printstacktrace();  // }   //对应方法二:图片已保存,只需读取就行了   try {   fileinputstream stream = new fileinputstream(photopath);   bitmap bitmap = bitmapfactory.decodestream(stream);   //预览图片   imageview image = findviewbyid(r.id.imageview);   image.setimagebitmap(bitmap);   //上传图片(android 4.0 之后不能在主线程中请求http请求)   file file = new file(photopath);   if (file.exists()) {   new thread(new runnable() {   @override   public void run() {    //文本字段(用于验证用户身份)    hashmap<string, string> form = new hashmap<string, string>();    form.put("username", "zhangqs");    form.put("password", "123456");    //图片字段    hashmap<string, string> file = new hashmap<string, string>();    file.put(pathhelper.getfilenamefrompath(photopath), photopath);    formupload(posturl, form, file);   }   }).start();   }   } catch (filenotfoundexception e) {   e.printstacktrace();   }   }   break;   // 选择图片库的图片   case choose:   if (resultcode == result_ok) {   try {   uri uri = data.getdata();   photopath = pathhelper.getrealpathfromuri(mainactivity.this, uri);   bitmap bitmap = mediastore.images.media.getbitmap(this.getcontentresolver(), uri);   //压缩图片   bitmap = scalebitmap(bitmap, (float) 0.5);   //预览图片   imageview image = findviewbyid(r.id.imageview);   image.setimagebitmap(bitmap);   //上传图片(android 4.0 之后不能在主线程中请求http请求)   file file = new file(photopath);   if (file.exists()) {   new thread(new runnable() {   @override   public void run() {    //文本字段(用于验证用户身份)    hashmap<string, string> form = new hashmap<string, string>();    form.put("username", "zhangqs");    form.put("password", "123456");    //图片字段    hashmap<string, string> file = new hashmap<string, string>();    file.put(pathhelper.getfilenamefrompath(photopath), photopath);    formupload(posturl, form, file);   }   }).start();   }   } catch (ioexception e) {   e.printstacktrace();   }   }   break;   }   }   //压缩图片   public bitmap scalebitmap(bitmap origin, float ratio) {   if (origin == null) {   return null;   }   int width = origin.getwidth();   int height = origin.getheight();   matrix matrix = new matrix();   matrix.prescale(ratio, ratio);   bitmap newbm = bitmap.createbitmap(origin, 0, 0, width, height, matrix, false);   return newbm;   }   //post 表单提交   @requiresapi(api = build.version_codes.o)   public static string formupload(string posturl, map<string, string> textmap, map<string, string> filemap) {   string res = "";   httpurlconnection conn = null;   string boundary = "---------------------------123821742118716"; //boundary就是request头和上传文件内容的分隔符   try {   url url = new url(posturl);   conn = (httpurlconnection) url.openconnection();   conn.setconnecttimeout(5000);   conn.setreadtimeout(30000);   conn.setdooutput(true);   conn.setdoinput(true);   conn.setusecaches(false);   conn.setrequestmethod("post");   conn.setrequestproperty("connection", "keep-alive");   conn.setrequestproperty("user-agent", "mozilla/5.0 (windows; u; windows nt 6.1; zh-cn; rv:1.9.2.6)");   conn.setrequestproperty("content-type", "multipart/form-data; boundary=" + boundary);   outputstream out = new dataoutputstream(conn.getoutputstream());   // text   if (textmap != null) {   stringbuffer buffer = new stringbuffer();   iterator iter = textmap.entryset().iterator();   while (iter.hasnext()) {   map.entry entry = (map.entry) iter.next();   string inputname = (string) entry.getkey();   string inputvalue = (string) entry.getvalue();   if (inputvalue == null) {   continue;   }   buffer.append("rn").append("--").append(boundary).append("rn");   buffer.append("content-disposition: form-data; name="" + inputname + ""rnrn");   buffer.append(inputvalue);   }   out.write(buffer.tostring().getbytes());   }   // file   if (filemap != null) {   iterator iter = filemap.entryset().iterator();   while (iter.hasnext()) {   map.entry entry = (map.entry) iter.next();   string inputname = (string) entry.getkey();   string inputvalue = (string) entry.getvalue();   if (inputvalue == null) {   continue;   }   file file = new file(inputvalue);   string filename = file.getname();   string contenttype = "";   if (filename.endswith(".jpg")) {   contenttype = "image/jpg";   } else if (filename.endswith(".png")) {   contenttype = "image/png";   } else if (contenttype == null || contenttype.equals("")) {   contenttype = "application/octet-stream";   }   stringbuffer buffer = new stringbuffer();   buffer.append("rn").append("--").append(boundary).append("rn");   buffer.append("content-disposition: form-data; name="" + inputname + ""; filename="" + filename + ""rn");   buffer.append("content-type:" + contenttype + "rnrn");   out.write(buffer.tostring().getbytes());   datainputstream in = new datainputstream(new fileinputstream(file));   int bytes = 0;   byte[] bufferout = new byte[1024];   while ((bytes = in.read(bufferout)) != -1) {   out.write(bufferout, 0, bytes);   }   in.close();   }   }   byte[] enddata = ("rn--" + boundary + "--rn").getbytes();   out.write(enddata);   out.flush();   out.close();   // 读取返回数据   stringbuffer buffer = new stringbuffer();   bufferedreader reader = new bufferedreader(new inputstreamreader(conn.getinputstream()));   string line = null;   while ((line = reader.readline()) != null) {   buffer.append(line).append("n");   }   res = buffer.tostring();   reader.close();   reader = null;   } catch (exception e) {   system.out.println("发送post请求出错。" + posturl);   e.printstacktrace();   } finally {   if (conn != null) {   conn.disconnect();   conn = null;   }   }   return res;   }  }

4.辅助类 pathhelper.java

  package com.qingshan.note;  import android.annotation.suppresslint;  import android.content.contenturis;  import android.content.context;  import android.database.cursor;  import android.net.uri;  import android.os.build;  import android.provider.documentscontract;  import android.provider.mediastore;  //android 路径辅助类  public class pathhelper {   //适配api19以下(不包括api19),根据uri获取图片的绝对路径   public static string getrealpathfromuri(context context, uri uri) {   int sdkversion = build.version.sdk_int;   if (sdkversion >= 19) { // api >= 19   return getrealpathfromuriaboveapi19(context, uri);   } else { // api < 19   return getrealpathfromuribelowapi19(context, uri);   }   }   /**   * 适配api19以下(不包括api19),根据uri获取图片的绝对路径   *   * @param context 上下文对象   * @param uri 图片的uri   * @return 如果uri对应的图片存在, 那么返回该图片的绝对路径, 否则返回null   */   private static string getrealpathfromuribelowapi19(context context, uri uri) {   return getdatacolumn(context, uri, null, null);   }   /**   * 适配api19及以上,根据uri获取图片的绝对路径   *   * @param context 上下文对象   * @param uri 图片的uri   * @return 如果uri对应的图片存在, 那么返回该图片的绝对路径, 否则返回null   */   @suppresslint("newapi")   private static string getrealpathfromuriaboveapi19(context context, uri uri) {   string filepath = null;   if (documentscontract.isdocumenturi(context, uri)) {   // 如果是document类型的 uri, 则通过document id来进行处理   string documentid = documentscontract.getdocumentid(uri);   if (ismediadocument(uri)) { // mediaprovider   // 使用':'分割   string id = documentid.split(":")[1];   string selection = mediastore.images.media._id + "=?";   string[] selectionargs = {id};   filepath = getdatacolumn(context, mediastore.images.media.external_content_uri, selection, selectionargs);   } else if (isdownloadsdocument(uri)) { // downloadsprovider   uri contenturi = contenturis.withappendedid(uri.parse("content://downloads/public_downloads"), long.valueof(documentid));   filepath = getdatacolumn(context, contenturi, null, null);   }   } else if ("content".equalsignorecase(uri.getscheme())) {   // 如果是 content 类型的 uri   filepath = getdatacolumn(context, uri, null, null);   } else if ("file".equals(uri.getscheme())) {   // 如果是 file 类型的 uri,直接获取图片对应的路径   filepath = uri.getpath();   }   return filepath;   }   private static string getdatacolumn(context context, uri uri, string selection, string[] selectionargs) {   string path = null;   string[] projection = new string[]{mediastore.images.media.data};   cursor cursor = null;   try {   cursor = context.getcontentresolver().query(uri, projection, selection, selectionargs, null);   if (cursor != null && cursor.movetofirst()) {   int columnindex = cursor.getcolumnindexorthrow(projection[0]);   path = cursor.getstring(columnindex);   }   } catch (exception e) {   e.printstacktrace();   } finally {   if (cursor != null) {   cursor.close();   }   }   return path;   }   private static boolean ismediadocument(uri uri) {   return "com.android.providers.media.documents".equals(uri.getauthority());   }   private static boolean isdownloadsdocument(uri uri) {   return "com.android.providers.downloads.documents".equals(uri.getauthority());   }   //从路径中提取文件名   public static string getfilenamefrompath(string path) {   int start = path.lastindexof("/");   int end = path.lastindexof(".");   if (start != -1 && end != -1) {   return path.substring(start + 1, end);   } else {   return null;   }   }  }

5.androidmanifest.xml

  <?xml version="1.0" encoding="utf-8"?>  <manifest xmlns:android="https://schemas.android.com/apk/res/android"   package="com.qingshan.note">   <!-- 因为拍照需要写入文件 所以需要申请读取内存的权限 -->   <uses-permission android:name="android.permission.camera" />   <uses-permission android:name="android.permission.write_external_storage" />   <uses-permission android:name="android.permission.access_network_state" />   <uses-permission android:name="android.permission.access_wifi_state"/>   <uses-permission android:name="android.permission.internet"/>   <application   android:networksecurityconfig="@xml/network_security_config"   android:allowbackup="true"   android:icon="@mipmap/ic_launcher"   android:label="@string/app_name"   android:usescleartexttraffic="true"   android:roundicon="@mipmap/ic_launcher_round"   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>   </application>  </manifest>

6.resxmlnetwork_security_config.xml

  <?xml version="1.0" encoding="utf-8"?>  <network-security-config>   <base-config cleartexttrafficpermitted="true" />   <domain-config cleartexttrafficpermitted="true" >   <domain includesubdomains="true">127.0.0.1</domain>   <domain includesubdomains="true">192.168.100.192</domain>   <domain includesubdomains="true">localhost</domain>   <domain includesubdomains="true">qingshanboke.com</domain>   </domain-config>  </network-security-config>

7.服务器端接收(asp.net mvc 接收)

       

  public actionresult andoriduploadfile()   {   var username = request.params["username"];   var password = request.params["password"];   if (string.isnullorempty(username) || string.isnullorempty(password))   {   return content("抱歉,用户名和密码错误!");   }   //todo:身份验证      var dir = pathhelper.getmappath("~/uploadfiles/" + datetime.now.tostring("yyyy-mm"));   if (!directory.exists(dir))   {   directory.createdirectory(dir);   }   for (int i = 0; i < request.files.count; i++)   {   var path = path.combine(dir, datetime.now.tostring("yyyymmddhhmmss") + ".jpg");   if (request.files[i] != null)   {   request.files[i].saveas(path);   }   }   return content("{"issuccess":true}");   }

三、注意事项

1.android发起http请求时,默认请求地址需https,需要增加 network-security-config 配置来允许使用http。(详见上面6.resxmlnetwork_security_config.xml)

2.发起post提交时,往往需要做接口身份识别,需要将文本字段和图片字段一起提交,构造表单时,需要 “content-type”, “multipart/form-data; boundary…”。

3.拍照时,默认只能取到缩略图,不够清晰,若要取到原图,需要在拍照时,传入指定保存位置,在回调函数中只需读取就可以了。

总结

以上所述是小编给大家介绍的android 拍照选择图片并上传功能的实现思路(包含权限动态获取),希望对大家有所帮助

本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。

ctvol管理联系方式QQ:251552304

本文章地址:https://www.ctvol.com/addevelopment/899800.html

(0)
上一篇 2021年10月23日
下一篇 2021年10月23日

精彩推荐