标签:
想必大家都在android中或多或少的使用过XUtils框架了吧,今天我们通过他来实现一个照片上传的Demo,希望能够对大家有帮助,下一篇再从源码角度来分析下XUtils的HttpUtils是怎么一个执行流程的;
先上执行效果图:
客户端实现:
首先来看布局文件:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="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" > <LinearLayout android:id="@+id/top" android:layout_width="wrap_content" android:layout_height="wrap_content"> <Button android:id="@+id/upload_image" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="上传图片" /> </LinearLayout> <ImageView android:id="@+id/imageView" android:layout_below="@id/top" android:layout_centerInParent="true" android:layout_width="wrap_content" android:layout_height="wrap_content"/> </RelativeLayout>很简单吧,就是一个按钮和一个用于显示图片的ImageView;
接下来是MainActivity,直接看onCreate方法:
@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方法:
/** * 初始化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行为选择对话框绑定点击监听事件,用来提示用户是通过拍照获得照片还是从图库获得,这个对象的定义如下:
/** * 显示选择图片来源的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方法来调用系统照相机:
/** * 调用相机来照相 * @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上面照片:
/** * 打开系统图库 * @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;
接下来我们看看不同的返回标志各自所执行的到底是什么内容呢?
@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方法:
/** * 将图片裁剪到指定大小 * @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方法,所以我们直接看他的代码就可以了:
/** * 将图片显示到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线程:
class UploadThread extends Thread { @Override public void run() { uploadPicture(); } }很简单,就只有一个uploadPicture方法了,自然我们需要查看uploadPicture的代码:
/** * 上传图片到数据库 */ 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(); } }); }这部分就是用到XUtils的HttpUtils的部分啦,首先设置一些请求参数,接着调用HttpUtils的send方法进行请求,为了能够对上传结果更加直观,我们添加了进度条,onStart方法是上传执行开始调用的方法,我们在此显示出进度条,onLoading是上传过程中执行的方法,大约每一秒钟会执行一次,我们在此通过Handler的消息机制将进度封装成Message对象将其发送给Handler处理,具体的更新进度条的代码是在我们上面的initView方法出现的,因为他只能在主线程中更新,最后在电泳失败或者成功之后都要调用ProgressDialog的dismiss方法来隐藏进度条;
最后就只剩下保存图片到SD卡的操作了,这个比较简单,就只是简单的文件存储操作了,只不过路径是SD卡而已:
/** * 将图片保存到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出来了:
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的配置,我的配置如下:
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5"> <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卡访问权限哈:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.INTERNET"></uses-permission> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"></uses-permission>点击这里下载源码!!!!!
android-----基于XUtils照片上传客户端以及服务器端实现
标签:
原文地址:http://blog.csdn.net/hzw19920329/article/details/51610683