Android-客户端上传多张图片到服务器功能实现

来源:互联网 发布:flexible.js官网 编辑:程序博客网 时间:2024/04/29 19:49

服务端:

需要两个jar包:

commons-fileupload-1.2.1.jar

commons-io-1.3.2.jar

代码:

<span style="font-family:KaiTi_GB2312;font-size:18px;">import java.io.BufferedInputStream;import java.io.File;import java.io.FileOutputStream;import java.io.IOException;import java.io.InputStream;import java.io.PrintWriter;import java.util.List;import javax.servlet.Servlet;import javax.servlet.ServletConfig;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import org.apache.commons.fileupload.FileItem;import org.apache.commons.fileupload.FileItemFactory;import org.apache.commons.fileupload.FileUploadException;import org.apache.commons.fileupload.disk.DiskFileItemFactory;import org.apache.commons.fileupload.servlet.ServletFileUpload;/** * 文件上传的Serlvet类 *  */public class FileImageUpload extends HttpServlet {private static final long serialVersionUID = 1L;private ServletFileUpload upload;private final long MAXSize = 4194304*2L;//4*2MBprivate String filedir;           /**     * @see HttpServlet#HttpServlet()     */    public FileImageUpload() {        super();        // TODO Auto-generated constructor stub    }/** * 设置文件上传的初始化信息 * @see Servlet#init(ServletConfig) */public void init(ServletConfig config) throws ServletException {// Create a factory for disk-based file itemsFileItemFactory factory = new DiskFileItemFactory();// Create a new file upload handlerthis.upload = new ServletFileUpload(factory);// Set overall request size constraint 4194304this.upload.setSizeMax(this.MAXSize);//File file = new File(pathname);filedir=config.getServletContext().getRealPath("images"); File file = new File(filedir);         if (!file.exists()) {             //创建临时目录         file.mkdir();         }}@SuppressWarnings("unchecked")protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {// TODO Auto-generated method stubPrintWriter out=response.getWriter();try {List<FileItem> items = this.upload.parseRequest(request);if(items!=null&& !items.isEmpty()){for (FileItem fileItem : items) {System.out.println(fileItem);String filename=fileItem.getName();String filepath=filedir+File.separator+filename;System.out.println("文件保存路径为:"+filepath);File file=new File(filepath);InputStream inputSteam=fileItem.getInputStream();BufferedInputStream fis=new BufferedInputStream(inputSteam);    FileOutputStream fos=new FileOutputStream(file);    int f;    while((f=fis.read())!=-1)    {       fos.write(f);    }    fos.flush();    fos.close();    fis.close();inputSteam.close();System.out.println("文件:"+filename+"上传成功!");}}System.out.println("上传文件成功!");out.write("上传文件成功!");} catch (FileUploadException e) {e.printStackTrace();out.write("上传文件失败:"+e.getMessage());}}}</span>



客户端:

布局代码:

activity_main.xml

<span style="font-family:KaiTi_GB2312;font-size:18px;"><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:id="@+id/container"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="vertical"    tools:context="com.example.girdviewtest.MainActivity" >    <!-- 显示图片 -->    <!-- 网格显示图片 行列间距5dp 每列宽度90dp -->    <GridView        android:id="@+id/gridView1"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:layout_margin="5dp"        android:layout_weight="111"        android:background="#EFDFDF"        android:columnWidth="90dp"        android:gravity="center"        android:horizontalSpacing="5dp"        android:numColumns="4"        android:stretchMode="columnWidth"        android:verticalSpacing="5dp" >    </GridView>    <!-- 底部按钮 -->    <LinearLayout        android:id="@+id/Layout_bottom"        android:layout_width="match_parent"        android:layout_height="50dp"        android:layout_alignParentBottom="true"        android:layout_weight="1"        android:orientation="horizontal" >        <Button            android:id="@+id/button1"            android:layout_width="wrap_content"            android:layout_height="fill_parent"            android:layout_weight="1"            android:onClick="button1"            android:text="上传"            android:textSize="20sp" />    </LinearLayout></LinearLayout></span>

griditem_addpic.xml

<span style="font-family:KaiTi_GB2312;font-size:18px;"><?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:gravity="center"    android:descendantFocusability="blocksDescendants"    android:orientation="vertical" >    <RelativeLayout        android:layout_gravity="center"        android:layout_width="85dp"        android:layout_height="85dp"        android:orientation="vertical" >        <ImageView  android:layout_marginTop="10dp"  android:layout_marginRight="10dp"            android:id="@+id/imageView1"            android:layout_width="fill_parent"            android:layout_height="fill_parent"            android:scaleType="fitXY"            android:src="@drawable/gridview_addpic" />    </RelativeLayout></LinearLayout></span>

Java代码:

UploadUtils.java

<span style="font-family:KaiTi_GB2312;font-size:18px;">package com.example.androiduploadfile;import java.io.DataOutputStream;import java.io.File;import java.io.FileInputStream;import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.net.HttpURLConnection;import java.net.MalformedURLException;import java.net.URL;import java.util.UUID;import android.util.Log;/** *  * 实现文件上传的工具类 */public class UploadUtils {private static final String TAG = "uploadFile";private static final int TIME_OUT = 10*10000000;   //超时时间private static final String CHARSET = "utf-8"; //设置编码public static final String SUCCESS="1";public static final String FAILURE="0";/** * android上传文件到服务器 * @param file  需要上传的文件 * @param RequestURL  请求的rul * @return  返回响应的内容 */public static String uploadFile(File file,String RequestURL){String  BOUNDARY =  UUID.randomUUID().toString();  //边界标识   随机生成String PREFIX = "--" , LINE_END = "\r\n"; String CONTENT_TYPE = "multipart/form-data";   //内容类型try {URL url = new URL(RequestURL);HttpURLConnection conn = (HttpURLConnection) url.openConnection();conn.setReadTimeout(TIME_OUT);conn.setConnectTimeout(TIME_OUT);conn.setDoInput(true);  //允许输入流conn.setDoOutput(true); //允许输出流conn.setUseCaches(false);  //不允许使用缓存conn.setRequestMethod("POST");  //请求方式conn.setRequestProperty("Charset", CHARSET);  //设置编码conn.setRequestProperty("connection", "keep-alive");   conn.setRequestProperty("Content-Type", CONTENT_TYPE + ";boundary=" + BOUNDARY); if(file!=null){/** * 当文件不为空,把文件包装并且上传 */OutputStream outputSteam=conn.getOutputStream();DataOutputStream dos = new DataOutputStream(outputSteam);StringBuffer sb = new StringBuffer();sb.append(PREFIX);sb.append(BOUNDARY);sb.append(LINE_END);/** * 这里重点注意: * name里面的值为服务器端需要key   只有这个key 才可以得到对应的文件 * filename是文件的名字,包含后缀名的   比如:abc.png   */sb.append("Content-Disposition: form-data; name=\"img\"; filename=\""+file.getName()+"\""+LINE_END); sb.append("Content-Type: application/octet-stream; charset="+CHARSET+LINE_END);sb.append(LINE_END);dos.write(sb.toString().getBytes());InputStream is = new FileInputStream(file);byte[] bytes = new byte[1024];int len = 0;while((len=is.read(bytes))!=-1){dos.write(bytes, 0, len);}is.close();dos.write(LINE_END.getBytes());byte[] end_data = (PREFIX+BOUNDARY+PREFIX+LINE_END).getBytes();dos.write(end_data);dos.flush();/** * 获取响应码  200=成功 * 当响应成功,获取响应的流   */int res = conn.getResponseCode();  Log.e(TAG, "response code:"+res);if(res==200){     return SUCCESS;}}} catch (MalformedURLException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}return FAILURE;}}</span>

UploadFileTask.java

使用到了异步加载

AsyncTask的用法 在开发Android应用时必须遵守单线程模型的原则:

 Android UI操作并不是线程安全的并且这些操作必须在UI线程中执行。在单线程模型中始终要记住两条法则: 1. 不要阻塞UI线程 2.确保只在UI线程中访问Android UI工具包 当一个程序第一次启动时,Android会同时启动一个对应的主线程(Main Thread),主线程主要负责处理与UI相关的事件,如:用户的按键事件,用户接触屏幕的事件以及屏幕绘图事件,并把相关的事件分发到对应的组件进行处理。所以主线程通常又被叫做UI线程。比如说从网上获取一个网页,在一个TextView中将其源代码显示出来,这种涉及到网络操作的程序一般都是需要开一个线程完成网络访问,但是在获得页面源码后, 是不能直接在网络操作线程中调用TextView.setText()的.因为其他线程中是不能直接访问主UI线程成员 。
 android提供了几种在其他线程中访问UI线程的方法。 Activity.runOnUiThread( Runnable ) View.post(Runnable ) View.postDelayed( Runnable, long ) Hanlder这些类或方法同样会使你的代码很复杂很难理解。然而当你需要实现一些很复杂的操作并需要频繁地更新UI时这会变得更糟糕。

 为了解决这个问题,Android 1.5提供了一个工具类:AsyncTask,它使创建需要与用户界面交互的长时间运行的任务变得更简单。相对来说AsyncTask更轻量级一些,适用于简单的异步处理,不需要借助线程和Handler即可实现。 AsyncTask是抽象类.AsyncTask定义了三种泛型类型 Params,Progress和Result。   Params 启动任务执行的输入参数,比如HTTP请求的URL。  

 Progress后台任务执行的百分比。   Result 后台执行任务最终返回的结果,比如String。

 AsyncTask的执行分为四个步骤,每一步都对应一个回调方法,这些方法不应该由应用程序调用,开发者需要做的就是实现这些方法。   

1)子类化AsyncTask   

2) 实现AsyncTask中定义的下面一个或几个方法   

 onPreExecute(), 该方法将在执行实际的后台操作前被UI thread调用。可以在该方法中做一些准备工作,如在界面上显示一个进度条。   

doInBackground(Params...), 将在onPreExecute方法执行后马上执行,该方法运行在后台线程中。这里将主要负责执行那些很耗时的后台计算工作。

可以调用publishProgress方法来更新实时的任务进度。该方法是抽象方法,子类必须实现。   
onProgressUpdate(Progress...),在publishProgress方法被调用后,UI thread将调用这个方法从而在界面上展示任务的进展情况,例如通过一个进度条进行展示。    onPostExecute(Result), 在doInBackground 执行完成后,onPostExecute 方法将被UI thread调用,后台的计算结果将通过该方法传递到UI thread.
 
  为了正确的使用AsyncTask类,以下是几条必须遵守的准则:   

1) Task的实例必须在UI thread中创建   

2)execute方法必须在UI thread中调用   

3) 不要手动的调用onPreExecute(), onPostExecute(Result),doInBackground(Params...),onProgressUpdate(Progress...)这几个方法   

4) 该task只能被执行一次,否则多次调用时将会出现异常doInBackground方法和onPostExecute的参数必须对应,这两个参数在AsyncTask声明的泛型参数列表中指定,第一个为doInBackground接受的参数 ,第二个为显示进度的参数,第第三个为doInBackground返回和onPostExecute传入的参数。

<span style="font-family:KaiTi_GB2312;font-size:18px;">package com.example.androiduploadfile;import java.io.File;import android.app.Activity;import android.app.AlertDialog;import android.app.ProgressDialog;import android.content.DialogInterface;import android.os.AsyncTask;import android.widget.Toast;public class UploadFileTask extends AsyncTask<String, Void, String> {public static final String requestURL = "http://192.168.47.114:8080/UploadFileWeb/servlet/FileImageUpload";/** * 可变长的输入参数,与AsyncTask.exucute()对应 */private ProgressDialog pdialog;private Activity context = null;public UploadFileTask(Activity ctx) {this.context = ctx;pdialog = ProgressDialog.show(context, "正在加载...", "系统正在处理您的请求");}@Overrideprotected void onPostExecute(String result) {// 返回HTML页面的内容pdialog.dismiss();if (UploadUtils.SUCCESS.equalsIgnoreCase(result)) {Toast.makeText(context, "上传成功!", Toast.LENGTH_SHORT).show();} else {Toast.makeText(context, "上传失败!", Toast.LENGTH_SHORT).show();}}@Overrideprotected void onPreExecute() {}@Overrideprotected void onCancelled() {super.onCancelled();}@Overrideprotected String doInBackground(String... params) {File file = new File(params[0]);return UploadUtils.uploadFile(file, requestURL);}@Overrideprotected void onProgressUpdate(Void... values) {}}</span>

MainActivity.java

<span style="font-family:KaiTi_GB2312;font-size:18px;">package com.example.androiduploadfile;import java.util.ArrayList;import java.util.HashMap;import java.util.List;import android.app.Activity;import android.app.AlertDialog;import android.app.AlertDialog.Builder;import android.content.DialogInterface;import android.content.Intent;import android.content.pm.ActivityInfo;import android.database.Cursor;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.net.Uri;import android.os.Bundle;import android.os.Handler;import android.os.Message;import android.provider.MediaStore;import android.text.TextUtils;import android.util.Log;import android.view.View;import android.view.WindowManager;import android.widget.AdapterView;import android.widget.AdapterView.OnItemClickListener;import android.widget.Button;import android.widget.GridView;import android.widget.ImageView;import android.widget.SimpleAdapter;import android.widget.SimpleAdapter.ViewBinder;import android.widget.Toast;/** *  * @author yu_longji * */public class MainActivity extends Activity {private GridView gridView1; // 网格显示缩略图private final int IMAGE_OPEN = 1; // 打开图片标记private String pathImage; // 选择图片路径private Bitmap bmp; // 导入临时图片private ArrayList<HashMap<String, Object>> imageItem;private SimpleAdapter simpleAdapter; // 适配器private List<String> list;String uploadFile = "";private Handler handler = new Handler() {@Overridepublic void handleMessage(Message msg) {// TODO Auto-generated method stubsuper.handleMessage(msg);switch (msg.what) {case 1:String b = (String) msg.obj;Toast.makeText(MainActivity.this, b, Toast.LENGTH_SHORT).show();break;case 0:String string = (String) msg.obj;Toast.makeText(MainActivity.this, string, Toast.LENGTH_SHORT).show();break;default:break;}}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);/* * 防止键盘挡住输入框 不希望遮挡设置activity属性 android:windowSoftInputMode="adjustPan" * 希望动态调整高度 android:windowSoftInputMode="adjustResize" */getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);// 锁定屏幕setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);setContentView(R.layout.activity_main);list = new ArrayList<>();// 获取控件对象gridView1 = (GridView) findViewById(R.id.gridView1);/* * 载入默认图片添加图片加号 通过适配器实现 SimpleAdapter参数imageItem为数据源 * R.layout.griditem_addpic为布局 */bmp = BitmapFactory.decodeResource(getResources(),R.drawable.gridview_addpic); // 加号imageItem = new ArrayList<HashMap<String, Object>>();HashMap<String, Object> map = new HashMap<String, Object>();map.put("itemImage", bmp);imageItem.add(map);simpleAdapter = new SimpleAdapter(this, imageItem,R.layout.griditem_addpic, new String[] { "itemImage" },new int[] { R.id.imageView1 });/* * HashMap载入bmp图片在GridView中不显示,但是如果载入资源ID能显示 如 map.put("itemImage", * R.drawable.img); 解决方法: 1.自定义继承BaseAdapter实现 2.ViewBinder()接口实现  */simpleAdapter.setViewBinder(new ViewBinder() {@Overridepublic boolean setViewValue(View view, Object data,String textRepresentation) {// TODO Auto-generated method stubif (view instanceof ImageView && data instanceof Bitmap) {ImageView i = (ImageView) view;i.setImageBitmap((Bitmap) data);return true;}return false;}});gridView1.setAdapter(simpleAdapter);/* * 监听GridView点击事件 报错:该函数必须抽象方法 故需要手动导入import android.view.View; */gridView1.setOnItemClickListener(new OnItemClickListener() {@Overridepublic void onItemClick(AdapterView<?> parent, View v,int position, long id) {if (position == 0) { // 点击图片位置为+ 0对应0张图片Toast.makeText(MainActivity.this, "添加图片",Toast.LENGTH_SHORT).show();// 选择图片Intent intent = new Intent(Intent.ACTION_PICK,android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);startActivityForResult(intent, IMAGE_OPEN);// 通过onResume()刷新数据} else {dialog(position);Toast.makeText(MainActivity.this,"点击第" + position + 1 + " 号图片", Toast.LENGTH_SHORT).show();}}});}// 获取图片路径 响应startActivityForResultprotected void onActivityResult(int requestCode, int resultCode, Intent data) {super.onActivityResult(requestCode, resultCode, data);// 打开图片if (resultCode == RESULT_OK && requestCode == IMAGE_OPEN) {Uri uri = data.getData();if (!TextUtils.isEmpty(uri.getAuthority())) {// 查询选择图片Cursor cursor = getContentResolver().query(uri,new String[] { MediaStore.Images.Media.DATA }, null,null, null);// 返回 没找到选择图片if (null == cursor) {return;}// 光标移动至开头 获取图片路径cursor.moveToFirst();pathImage = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));list.add(pathImage);}} // end if 打开图片}// 提交发布public void button1(View view) {for (int i = 0; i < list.size(); i++) {uploadFile = list.get(i);System.out.println(uploadFile);if (uploadFile != null && uploadFile.length() > 0) {UploadFileTask uploadFileTask = new UploadFileTask(this);uploadFileTask.execute(uploadFile);}}}// 刷新图片@Overrideprotected void onResume() {super.onResume();if (!TextUtils.isEmpty(pathImage)) {Bitmap addbmp = BitmapFactory.decodeFile(pathImage);HashMap<String, Object> map = new HashMap<String, Object>();map.put("itemImage", addbmp);imageItem.add(map);simpleAdapter = new SimpleAdapter(this, imageItem,R.layout.griditem_addpic, new String[] { "itemImage" },new int[] { R.id.imageView1 });simpleAdapter.setViewBinder(new ViewBinder() {@Overridepublic boolean setViewValue(View view, Object data,String textRepresentation) {// TODO Auto-generated method stubif (view instanceof ImageView && data instanceof Bitmap) {ImageView i = (ImageView) view;i.setImageBitmap((Bitmap) data);return true;}return false;}});gridView1.setAdapter(simpleAdapter);simpleAdapter.notifyDataSetChanged();// 刷新后释放防止手机休眠后自动添加pathImage = null;}}/* * Dialog对话框提示用户删除操作 position为删除图片位置 */protected void dialog(final int position) {String string = "是否删除该图片";AlertDialog.Builder builder = new Builder(MainActivity.this);builder.setMessage(string);builder.setTitle("提示");builder.setPositiveButton("确认", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {dialog.dismiss();imageItem.remove(position);list.remove(position - 1);simpleAdapter.notifyDataSetChanged();}});builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {dialog.dismiss();}});builder.create().show();}}</span>



1 0