android-----基于XUtils照片上传客户端以及服务器端实现

想必大家都在android中或多或少的使用过XUtils框架了吧,今天我们通过他来实现一个照片上传的Demo,希望能够对大家有帮助,下一篇再从源码角度来分析下XUtils的HttpUtils是怎么一个执行流程的;

先上执行效果图:

\\\\

客户端实现:

首先来看布局文件:

 

?
1
2
3
4
5
6
7
8
<relativelayout android:layout_height= "match_parent" android:layout_width= "match_parent" android:paddingbottom= "@dimen/activity_vertical_margin" android:paddingleft= "@dimen/activity_horizontal_margin" android:paddingright= "@dimen/activity_horizontal_margin" android:paddingtop= "@dimen/activity_vertical_margin" tools:context= ".MainActivity" xmlns:android= "http://schemas.android.com/apk/res/android" xmlns:tools= "http://schemas.android.com/tools" >
     <linearlayout android:id= "@+id/top" android:layout_height= "wrap_content" android:layout_width= "wrap_content" ><button android:id= "@+id/upload_image" android:layout_height= "wrap_content" android:layout_width= "wrap_content" android:text= "上传图片" >
      
         
     <imageview android:id= "@+id/imageView" android:layout_below= "@id/top" android:layout_centerinparent= "true" android:layout_height= "wrap_content" android:layout_width= "wrap_content" >
     
 
</imageview></button></linearlayout></relativelayout>
很简单吧,就是一个按钮和一个用于显示图片的ImageView;

 

接下来是MainActivity,直接看onCreate方法:

 

?
1
2
3
4
5
6
7
8
@Override
     protected void onCreate(Bundle savedInstanceState) {
         super .onCreate(savedInstanceState);
         setContentView(R.layout.activity_main);
         initView();
         httpUtils = new HttpUtils( 100000 );
         httpUtils.configCurrentHttpCacheExpiry( 5000 );
     }
这个方法首先会调用initView来初始化界面,接着创建了一个HttpUtils对象,并且设置他的连接超时时间是100s,设置他的缓存有效时间是5s,来看看initView方法:

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/**
      * 初始化view控件
      */
     public void initView()
     {
         uploadImageBt = (Button) findViewById(R.id.upload_image);
         imageView = (ImageView)findViewById(R.id.imageView);
         uploadImageBt.setOnClickListener( this );
         progressDialog = getProgressDialog(); //获得进度条
         dialogListener = new DialogInterface.OnClickListener() {
             
             @Override
             public void onClick(DialogInterface dialog, int which) {
                 switch (which) {
                 case 0 :
                     tempFile = new File(Environment.getExternalStorageDirectory(),getPhotoFileName());
                     //调用系统拍照
                     startCamera(dialog);
                     break ;
                 case 1 :
                     //打开系统图库
                     startWall(dialog);
                     break ;
                 default :
                     break ;
                 }
             }
         };
         mHandler = new Handler(){
             @Override
             public void handleMessage(Message msg) {
                 if (msg.arg1 > 0 )
                     progressDialog.setProgress(msg.arg1); //更新进度条
             }
         };
     }

首先第9行获得一个ProgressDialog对象,第10行为选择对话框绑定点击监听事件,用来提示用户是通过拍照获得照片还是从图库获得,这个对象的定义如下:

 

?
1
2
3
4
5
6
7
8
9
10
11
/**
      * 显示选择图片来源的dialog(来自拍照还是本地图库)
      * @param title
      * @param items
      */
     public void showDialog(String title,String[] items)
     {
         AlertDialog.Builder dialog = new AlertDialog.Builder( this ).setTitle(title).setItems(items, dialogListener);
         //显示dialog
         dialog.show();
     }
如果选择拍照,则通过startCamera方法来调用系统照相机:

 

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
      * 调用相机来照相
      * @param dialog
      */
     public void startCamera(DialogInterface dialog)
     {
         dialog.dismiss(); //首先隐藏选择照片来源的dialog
         //调用系统的拍照功能
         Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
         intent.putExtra( "camerasensortype" , 2 ); //调用前置摄像头
         intent.putExtra( "autofocus" , true ); //进行自动对焦操作
         intent.putExtra( "fullScreen" , false ); //设置全屏
         intent.putExtra( "showActionIcons" , false );
         intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(tempFile)); //指定调用相机之后所拍照存储到的位置
         startActivityForResult(intent, PHOTO_CAMERA);
     }
该方法首先会将选择对话框隐藏,接着开启系统摄像头并且进行相应的设置,并且指定保存照片的位置,最后在返回上一个Activity之前将照片结果封装在intent中返回,并且设置返回标志是PHOTO_CAMERA;

 

如果选择的是相册的话,则通过调用startWall方法来获得SD上面照片:

 

?
1
2
3
4
5
6
7
8
9
10
11
/**
      * 打开系统图库
      * @param dialog
      */
     public void startWall(DialogInterface dialog)
     {
         dialog.dismiss(); //设置隐藏dialog
         Intent intent = new Intent(Intent.ACTION_PICK,android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
         intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*" );
         startActivityForResult(intent, PHOTO_WALL);
     }
该方法同样首先将选择对话框隐藏,接着调用系统服务显示出SD卡中所有存在的图片,并且通过intent返回图片信息,同时设置返回标志为PHOTO_WALL;

 

接下来我们看看不同的返回标志各自所执行的到底是什么内容呢?

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@Override
     protected void onActivityResult( int requestCode, int resultCode, Intent data) {
         super .onActivityResult(requestCode, resultCode, data);
         switch (requestCode) {
         case PHOTO_CAMERA:
             //表示从相机获得的照片,需要进行裁剪
             startPhotoCut(Uri.fromFile(tempFile), 300 , true );
             break ;
         case PHOTO_WALL:
             if ( null != data)
                 startPhotoCut(data.getData(), 300 , false );
             break ;
         case PHOTO_STORE:
             if ( null != data)
             {
                 setPictureToImageView(data, true );
             }
             break ;
         case PHOTO_NOT_STORE:
             if ( null != data)
             {
                 setPictureToImageView(data, false );
             }
             break ;
         default :
             break ;
         }
     }
该方法是在调用startActivityForResult之后由系统调用的,看到了我们刚刚见到的PHOTO_CAMREA以及PHOTO_WALL标志,首选来看看PHOTO_CAMREA,他会调用startPhotoCut对照片进行裁剪,来看看startPhotoCut方法:

 

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/**
      * 将图片裁剪到指定大小
      * @param uri
      * @param size
      * @param flag
      */
     public void startPhotoCut(Uri uri, int size, boolean flag)
     {
         Intent intent = new Intent( "com.android.camera.action.CROP" );
         intent.setDataAndType(uri, "image/*" );
         intent.putExtra( "crop" , true ); //设置Intent中的view是可以裁剪的
         //设置宽高比
         intent.putExtra( "aspectX" , 1 );
         intent.putExtra( "aspectY" , 1 );
         //设置裁剪图片的宽高
         intent.putExtra( "outputX" , size);
         intent.putExtra( "outputY" , size);
         //设置是否返回数据
         intent.putExtra( "return-data" , true );
         if (flag == true )
             startActivityForResult(intent, PHOTO_STORE);
         else
         {
             tempIntent = intent;
             try {
                 startActivityForResult(tempIntent, PHOTO_NOT_STORE);
                 System.out.println( "haha" );
             } catch (Exception e) {
                 System.out.println(e.toString());
             }
         }
     }
这个方法前面的几行都是对照片进行裁剪的一些设置,接着通过flag标志来判断是否将照片存储到SD卡上面,因为如果flag为false的话,表示这张照片是我们通过图库获取的,他来自于SD卡,因此没什么必要再存一次了,因而返回标志PHOTO_NOT_STORE,如果flag为true的话,才需要存储一遍,返回标志PHOTO_STORE,同样的调用的是startActivityForResult方法,那么他也会执行onActivityResult方法;

 

那么对于PHOTO_WALL标志,如果选择的图片不为空的话,则执行startPhotoCut方法,同样也进行裁剪;

对于PHOTO_NOT_STORE和PHOTO_STORE标志,他们都会执行setPictureToImageView方法,所以我们直接看他的代码就可以了:

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
/**
      * 将图片显示到ImageView上面
      * @param data
      * @param flag 表示如果是拍照获得的照片的话则是true,如果是从系统选择的照片的话就是false
      */
     public void setPictureToImageView(Intent data, boolean flag)
     {
         Bundle bundle = data.getExtras();
         if ( null != bundle)
         {
             Bitmap bitmap = bundle.getParcelable( "data" );
             imageView.setImageBitmap(bitmap); //将图片显示到ImageView上面
             //上传图片到服务器
             if (flag == false )
             {
                 //需要首先修改tempFile的值
                 String path = getSelectPhotoPath(tempIntent);
                 System.out.println( "path:  " +path);
                 tempFile = new File(path);
                 //uploadPicture();
                 //上传图片
                 UploadThread thread = new UploadThread();
                 thread.start();
             } else
             {
                 //uploadPicture();
                 //上传图片
                 UploadThread thread = new UploadThread();
                 thread.start();
             }
             if (flag == true )
                 savePictureToSD(bitmap); //保存图片到sd卡上面
         }
     }
这个方法做的事比较多,首先呢,他会从intent中获取到获取到图片并且显示到ImageView上面,接着会调用UploadThread线程将图片上传到服务器上面,最如果flag为true的话表示需要将图片存储到本地,那么我们需要调用savePictureToSD来存储图片,先来看看UploadThread线程:

 

 

?
1
2
3
4
5
6
7
class UploadThread extends Thread
     {
         @Override
         public void run() {
             uploadPicture();
         }
     }
很简单,就只有一个uploadPicture方法了,自然我们需要查看uploadPicture的代码:

 

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
/**
      * 上传图片到数据库
      */
     public void uploadPicture()
     {
         RequestParams params = new RequestParams();
         params.addBodyParameter( "msg" ,tempFile.getAbsolutePath());
         params.addBodyParameter(tempFile.getPath().replace( "/" , "" ), tempFile);
         httpUtils.send(HttpMethod.POST, url,params, new RequestCallBack<string>() {
             
             @Override
             public void onStart() {
                 progressDialog.show(); //显示进度条
             }
             @Override
             public void onFailure(HttpException arg0, String arg1) {
                 System.out.println( "上传失败" );
                 System.out.println(arg0.toString());
                 //上传失败之后隐藏进度条
                 progressDialog.dismiss();
             }
             @Override
             public void onLoading( long total, long current, boolean isUploading) {
                 System.out.println( "current/total:  " +current+ "/" +total);
                 int process = 0 ;
                 if (total != 0 )
                 {
                     process = ( int )(current/(total/ 100 ));
                 }
                 Message message = new Message();
                 message.arg1 = process;
                 mHandler.sendMessage(message);
                 super .onLoading(total, current, isUploading);
             }
             @Override
             public void onSuccess(ResponseInfo<string> arg0) {
                 System.out.println( "上传成功" );
                 //上传成功之后隐藏进度条
                 progressDialog.dismiss();
             }
         });
     }</string></string>
这部分就是用到XUtils的HttpUtils的部分啦,首先设置一些请求参数,接着调用HttpUtils的send方法进行请求,为了能够对上传结果更加直观,我们添加了进度条,onStart方法是上传执行开始调用的方法,我们在此显示出进度条,onLoading是上传过程中执行的方法,大约每一秒钟会执行一次,我们在此通过Handler的消息机制将进度封装成Message对象将其发送给Handler处理,具体的更新进度条的代码是在我们上面的initView方法出现的,因为他只能在主线程中更新,最后在电泳失败或者成功之后都要调用ProgressDialog的dismiss方法来隐藏进度条;

 

最后就只剩下保存图片到SD卡的操作了,这个比较简单,就只是简单的文件存储操作了,只不过路径是SD卡而已:

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/**
      * 将图片保存到SD卡上面
      * @param bitmap
      */
     public void savePictureToSD(Bitmap bitmap)
     {
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
         FileOutputStream fos = null ;
         bitmap.compress(Bitmap.CompressFormat.JPEG, 100 , baos); //第2个参数表示压缩率,100表示不压缩
         try {
             fos = new FileOutputStream(tempFile);
             fos.write(baos.toByteArray());
             fos.flush();
         } catch (Exception e) {
             e.printStackTrace();
         } finally {
             try {
                 if ( null != baos)
                 {
                     baos.close();
                     baos = null ;
                 }
                 if ( null != fos)
                 {
                     fos.close();
                     fos = null ;
                 }
             } catch (Exception e2) {
                 
             }
         }
     }
至此,客户端代码讲解完毕,接下来是服务器端代码:

 

服务器端:

代码比较少,直接copy出来了:

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class UploadServlet extends HttpServlet {
 
     public void doGet(HttpServletRequest request, HttpServletResponse response)
             throws ServletException, IOException {
 
         doPost(request, response);
     }
 
     public void doPost(HttpServletRequest request, HttpServletResponse response)
             throws ServletException, IOException {
 
         request.setCharacterEncoding( "utf-8" );
         response.setCharacterEncoding( "utf-8" );
         response.setContentType( "text/html,charset=utf-8" );
         SmartUpload smartUpload = new SmartUpload();
         String msg= null ;
         try
             smartUpload.initialize( this .getServletConfig(), request, response);
             smartUpload.upload(); 
             msg = smartUpload.getRequest().getParameter( "msg" );
             System.out.println(smartUpload.getFiles().getCount());
             com.jspsmart.upload.File smartFile = smartUpload.getFiles().getFile( 0 ); 
             if (!smartFile.isMissing()) { 
                 String saveFileName = getServletContext().getRealPath( "/" )+ "images\\" + smartFile.getFileName(); 
                 System.out.println(saveFileName);
                 smartFile.saveAs(saveFileName, SmartUpload.SAVE_PHYSICAL); 
             }
         } catch (Exception e) { 
             e.printStackTrace();
         }
     }
}

最主要是doPost方法了,本实例采用的是SmartUpload的方式来实现文件上传操作的,当然你也可以采用别的方法,至于jar包等会源码下载链接的工程里面就有啦,第18行首先对SmartUpload进行初始化,接着调用upload方法准备上传,第22行获得客户端上传文件的第一个文件,从这里我们也可以看出来SmartUpload是支持多文件上传的,接着第23行判断这个文件是否存在,24行生成存放文件的路径,26行进行文件的存储操作,注意SmartUpload.SAVE_PHYSICAL的意思指的是绝对路径,因此你的文件存储路径必须是全路径,这样服务器端代码讲解结束,是不是很简单呀,提醒一下web.xml的配置,我的配置如下:

 

?
1
2
3
4
5
6
7
8
9
10
11
<!--?xml version= "1.0" encoding= "UTF-8" ?-->
     <servlet>
         <servlet-name>UploadServlet</servlet-name>
         <servlet- class >com.hzw.servlet.UploadServlet</servlet- class >
     </servlet>
     <servlet-mapping>
         <servlet-name>UploadServlet</servlet-name>
         <url-pattern>/upload</url-pattern>
     </servlet-mapping>
</web-app>

基本上讲解结束啦,记得在客户端里面别忘记添加网络访问和SD卡访问权限哈:


本文 由微信妈妈ontaobao.cn(公众号买卖) 转载文章来源 http://www.2cto.com/kf/201606/516043.html

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值