Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> android-----基於XUtils照片上傳客戶端以及服務器端實現

android-----基於XUtils照片上傳客戶端以及服務器端實現

編輯:關於Android編程

想必大家都在android中或多或少的使用過XUtils框架了吧,今天我們通過他來實現一個照片上傳的Demo,希望能夠對大家有幫助,下一篇再從源碼角度來分析下XUtils的HttpUtils是怎麼一個執行流程的;

先上執行效果圖:

\\\\

客戶端實現:

首先來看布局文件:

 


    
很簡單吧,就是一個按鈕和一個用於顯示圖片的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() {
			
			@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 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的配置,我的配置如下:

 



	
		UploadServlet
        com.hzw.servlet.UploadServlet
	
	
    	UploadServlet
        /upload
    
基本上講解結束啦,記得在客戶端裡面別忘記添加網絡訪問和SD卡訪問權限哈:
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved