首先在layout文件中布局如下,很简单就是一个ImageView用于显示图片,一个按钮拍照,一个按钮上传
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_up_photo" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:gravity="center" tools:context="com.example.sportrecord.UpPhoto"> <ImageView android:id="@+id/photoshow" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <Button android:layout_marginLeft="10dp" android:text="拍照打卡" android:id="@+id/takephoto" android:layout_width="180dp" android:layout_height="match_parent" /> <Button android:layout_marginRight="0dp" android:text="确认上传" android:id="@+id/upphoto" android:layout_width="180dp" android:layout_height="match_parent" /> </LinearLayout> </LinearLayout>
然后再activity的java文件中代码如下
package com.example.sportrecord; import android.app.Activity; import android.content.Intent; import android.content.SharedPreferences; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.net.Uri; import android.os.Build; import android.provider.MediaStore; import android.support.v4.content.FileProvider; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.util.Base64; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.ImageView; import android.widget.Toast; import com.example.sportrecord.Util.BitmapUtils; import com.example.sportrecord.Util.NetUtil; import java.io.ByteArrayOutputStream; import java.io.File; public class UpPhoto extends Activity { public static final int TAKE_PHOTO=1; private ImageView photoshow; private Uri imageUri; private Button takephoto; private Button upphoto; public Bitmap bitmap1; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_up_photo); takephoto=(Button)findViewById(R.id.takephoto); upphoto=(Button)findViewById(R.id.upphoto); photoshow=(ImageView)findViewById(R.id.photoshow); Intent intent=getIntent(); String point=intent.getStringExtra("point"); takephoto.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { File outputImage =new File(getExternalCacheDir(),"output_image.jpg");//将拍照文件存在本地 try{ if (outputImage.exists()){ outputImage.delete(); } outputImage.createNewFile(); }catch (Exception e){ } if(Build.VERSION.SDK_INT>=24){ imageUri= FileProvider.getUriForFile(UpPhoto.this,"com.example.SportRecord.fileprovider",outputImage);//fileprovider下面会定义 }else { imageUri=Uri.fromFile(outputImage); } Intent intent=new Intent("android.media.action.IMAGE_CAPTURE");//打开相机 intent.putExtra(MediaStore.EXTRA_OUTPUT,imageUri); startActivityForResult(intent,TAKE_PHOTO); } }); upphoto.setOnClickListener(new View.OnClickListener() {//上传图片 @Override public void onClick(View v) { if (bitmap1==null){ Toast.makeText(UpPhoto.this,"无图片",Toast.LENGTH_LONG).show(); }else { SharedPreferences pref=getSharedPreferences("userMessage",MODE_PRIVATE);//获取用户id,项目需要,例子中直接删了就好 String useraccount=pref.getString("useraccount",""); new Thread(new Runnable() { @Override public void run() { NetUtil.getPhotoMes(UpPhoto.this,bitmap1,useraccount,point);//NetUtil.getPhotoMes是一个自定义函数用来和服务器连接,下面有代码 } }).start(); Toast.makeText(UpPhoto.this,"成功",Toast.LENGTH_LONG).show(); UpPhoto.this.finish(); } } }); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) {//显示图片startActivityForResult启动的intent会自动跳到这里 switch (requestCode) { case TAKE_PHOTO: if (resultCode == RESULT_OK) { try { Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(imageUri)); bitmap1= BitmapUtils.zoomImage(bitmap,400,550);//压缩图片,下面也有源码 photoshow.setImageBitmap(bitmap1); } catch (Exception e) { } } break; default: break; } } }
服务器连接函数NetUtil.getPhotoMes
public static void getPhotoMes(Context cont, Bitmap photodata, String user_account, String point) { try { ByteArrayOutputStream baos = new ByteArrayOutputStream(); //将bitmap一字节流输出 Bitmap.CompressFormat.PNG 压缩格式,100:压缩率,baos:字节流 photodata.compress(Bitmap.CompressFormat.PNG, 100, baos); baos.close(); byte[] buffer = baos.toByteArray(); System.out.println("图片的大小:"+buffer.length); //将图片的字节流数据加密成base64字符输出 String photo = Base64.encodeToString(buffer, 0, buffer.length,Base64.DEFAULT); //photo=URLEncoder.encode(photo,"UTF-8"); RequestParams params = new RequestParams(); params.put("photo", photo); params.put("user_account", user_account);//传输的字符数据 params.put("point", point);//传输的字符数据 String url = "服务器sevlet地址"; AsyncHttpClient client = new AsyncHttpClient(); client.post(url, params, new AsyncHttpResponseHandler() { }); } catch (Exception e) { e.printStackTrace(); } }
/图片压缩函数BitmapUtils.zoomImage,我给的函数较多,我使用的是zoomImage这个函数输入一个bitmap返回一个压缩后的bitmap,嫌累直接看zoomImage就好
package com.example.sportrecord.Util; import android.content.Context; import android.content.CursorLoader; import android.content.res.Resources; import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Matrix; import android.net.Uri; import android.provider.MediaStore; public class BitmapUtils { private static int mDesiredWidth; private static int mDesiredHeight; /** * @description 从Resources中加载图片 * * @param res * @param resId * @param reqWidth * @param reqHeight * @return */ public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight) { BitmapFactory.Options options = new BitmapFactory.Options(); //Bitmap bmp = BitmapFactory.decodeFile(path,options); // 设置成了true,不占用内存,只获取bitmap宽高 options.inJustDecodeBounds = true; // 初始化options对象 BitmapFactory.decodeResource(res, resId, options); // 得到计算好的options,目标宽、目标高 options = getBestOptions(options, reqWidth, reqHeight); Bitmap src = BitmapFactory.decodeResource(res, resId, options); // 载入一个稍大的缩略图 return createScaleBitmap(src, mDesiredWidth, mDesiredHeight); // 进一步得到目标大小的缩略图 } public static Bitmap zoomImage(Bitmap bgimage, double newWidth, double newHeight) { // 获取这个图片的宽和高 float width = bgimage.getWidth(); float height = bgimage.getHeight(); // 创建操作图片用的matrix对象 Matrix matrix = new Matrix(); // 计算宽高缩放率 float scaleWidth = ((float) newWidth) / width; float scaleHeight = ((float) newHeight) / height; // 缩放图片动作 matrix.postScale(scaleWidth, scaleHeight); Bitmap bitmap = Bitmap.createBitmap(bgimage, 0, 0, (int) width, (int) height, matrix, true); return bitmap; } /** * @description 从SD卡上加载图片 * * @param pathName * @param reqWidth * @param reqHeight * @return */ public static Bitmap decodeSampledBitmapFromFile(String pathName, int reqWidth, int reqHeight) { BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeFile(pathName, options); options = getBestOptions(options, reqWidth, reqHeight); Bitmap src = BitmapFactory.decodeFile(pathName, options); return createScaleBitmap(src, mDesiredWidth, mDesiredHeight); } /** * @description 计算目标宽度,目标高度,inSampleSize * * @param options * @param reqWidth * @param reqHeight * @return BitmapFactory.Options对象 */ private static BitmapFactory.Options getBestOptions(BitmapFactory.Options options, int reqWidth, int reqHeight) { // 读取图片长宽 int actualWidth = options.outWidth; int actualHeight = options.outHeight; // Then compute the dimensions we would ideally like to decode to. mDesiredWidth = getResizedDimension(reqWidth, reqHeight, actualWidth, actualHeight); mDesiredHeight = getResizedDimension(reqHeight, reqWidth, actualHeight, actualWidth); // 根据现在得到计算inSampleSize options.inSampleSize = calculateBestInSampleSize(actualWidth, actualHeight, mDesiredWidth, mDesiredHeight); // 使用获取到的inSampleSize值再次解析图片 options.inJustDecodeBounds = false; return options; } /** * Scales one side of a rectangle to fit aspect ratio. 最终得到重新测量的尺寸 * * @param maxPrimary * Maximum size of the primary dimension (i.e. width for max * width), or zero to maintain aspect ratio with secondary * dimension * @param maxSecondary * Maximum size of the secondary dimension, or zero to maintain * aspect ratio with primary dimension * @param actualPrimary * Actual size of the primary dimension * @param actualSecondary * Actual size of the secondary dimension */ private static int getResizedDimension(int maxPrimary, int maxSecondary, int actualPrimary, int actualSecondary) { double ratio = (double) actualSecondary / (double) actualPrimary; int resized = maxPrimary; if (resized * ratio > maxSecondary) { resized = (int) (maxSecondary / ratio); } return resized; } /** * Returns the largest power-of-two divisor for use in downscaling a bitmap * that will not result in the scaling past the desired dimensions. * * @param actualWidth * Actual width of the bitmap * @param actualHeight * Actual height of the bitmap * @param desiredWidth * Desired width of the bitmap * @param desiredHeight * Desired height of the bitmap */ // Visible for testing. private static int calculateBestInSampleSize(int actualWidth, int actualHeight, int desiredWidth, int desiredHeight) { double wr = (double) actualWidth / desiredWidth; double hr = (double) actualHeight / desiredHeight; double ratio = Math.min(wr, hr); float inSampleSize = 1.0f; while ((inSampleSize * 2) <= ratio) { inSampleSize *= 2; } return (int) inSampleSize; } private static Bitmap createScaleBitmap(Bitmap tempBitmap, int desiredWidth, int desiredHeight) { // If necessary, scale down to the maximal acceptable size. if (tempBitmap != null && (tempBitmap.getWidth() > desiredWidth || tempBitmap.getHeight() > desiredHeight)) { // 如果是放大图片,filter决定是否平滑,如果是缩小图片,filter无影响 Bitmap bitmap = Bitmap.createScaledBitmap(tempBitmap, desiredWidth, desiredHeight, true); tempBitmap.recycle(); // 释放Bitmap的native像素数组 return bitmap; } else { return tempBitmap; // 如果没有缩放,那么不回收 } } public static String getRealPathFromUri(Context context, Uri uri) { if (context == null || uri == null) { return null; } if ("file".equalsIgnoreCase(uri.getScheme())) { return getRealPathFromUri_Byfile(context, uri); } else if ("content".equalsIgnoreCase(uri.getScheme())) { return getRealPathFromUri_Api11To18(context, uri); } return null; } public static String getRealPathFromUri_Byfile(Context context,Uri uri){ String uri2Str = uri.toString(); String filePath = uri2Str.substring(uri2Str.indexOf(":") + 3); return filePath; } private static String getRealPathFromUri_Api11To18(Context context, Uri uri) { String filePath = null; String[] projection = { MediaStore.Images.Media.DATA }; CursorLoader loader = new CursorLoader(context, uri, projection, null, null, null); Cursor cursor = loader.loadInBackground(); if (cursor != null) { cursor.moveToFirst(); filePath = cursor.getString(cursor.getColumnIndex(projection[0])); cursor.close(); } return filePath; } }
然后我们需要一些配置
在androidManifest中加入一个provider,写在appliation中
<provider android:exported="false" android:grantUriPermissions="true" android:authorities="com.example.SportRecord.fileprovider" android:name="android.support.v4.content.FileProvider"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths"/> </provider>
然后右击res建立新目录xml文件夹然后创建一个file_paths.xml文件代码如下
<?xml version="1.0" encoding="utf-8"?> <paths xmlns:android="http://schemas.android.com/apk/res/android"> <external-path name="my_images" path=""></external-path> </paths>
最后需要声明下权限androidManifest中
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <!-- SD卡权限 -->
接下来是服务器代码
package org.java.servlet; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.Date; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.java.write.WriteMorningWay; import sun.misc.BASE64Decoder; /** * Servlet implementation class getPhotoMes */ @WebServlet("/getPhotoMes") public class getPhotoMes extends HttpServlet { private static final long serialVersionUID = 1L; /** * @see HttpServlet#HttpServlet() */ public getPhotoMes() { super(); // TODO Auto-generated constructor stub } /** * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response) */ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // TODO Auto-generated method stub request.setCharacterEncoding("utf-8"); response.setCharacterEncoding("utf-8"); response.setContentType("text/html"); String photo = request.getParameter("photo"); String user_account = request.getParameter("user_account"); String point = request.getParameter("point"); try { // 对base64数据进行解码 生成 字节数组,不能直接用Base64.decode();进行解密 byte[] photoimg = new BASE64Decoder().decodeBuffer(photo); for (int i = 0; i < photoimg.length; ++i) { if (photoimg[i] < 0) { // 调整异常数据 photoimg[i] += 256; } } // byte[] photoimg = Base64.decode(photo);//此处不能用Base64.decode()方法解密,我调试时用此方法每次解密出的数据都比原数据大 所以用上面的函数进行解密,在网上直接拷贝的,花了好几个小时才找到这个错误(菜鸟不容易啊) String savePath = this.getServletContext().getRealPath("/photoUpload/"); SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmss");//设置日期格式来命名图片 String string=df.format(new Date()); String photoname=string+user_account+".png"; System.out.print("length:"+savePath); File file = new File(savePath, photoname); if (!file.exists()) { file.createNewFile(); } FileOutputStream out = new FileOutputStream(file); out.write(photoimg); out.flush(); out.close(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } /** * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response) */ protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // TODO Auto-generated method stub doGet(request, response); } }