android 附件(图片)上传下载功能开发

Android multipart 上传文件到服务端 那么multipart中的文件名是如何取出的? 阅读详情

说明:附件上传,选择上传的附件只做了从图库选择图片或者拍照获取图片,实际也可以上传其他类型的文件,附件分为11中状态,未上传,准备上传,上传中,上传失败,上传成功,未下载,准备下载,下载中,下载成功,下载失败,已和服务器同步,看似有点复杂,其实也挺简单。

下面我将从无到有,一个一个伴随着效果图以及代码片段进行讲解

一:空白的附件列表界面如下:



点击“添加图片”按钮后:会弹出如下的选择菜单,提示是从图库选择图片还是拍照获取图片
二:选择图片菜单界面如下:





三:当选择4张图片或者拍照得到图片后,显示界面如下所示:

可以看到每个条目有一张缩略图,文件名,文件大小,最后修改日期,文件状态,以及右侧的操作按钮组成,文件状态还有一个隐藏的TextView,可以用来显示上传或下载的进度


四:点击题目中的文件名,可以修改文件的名字(只有未上传状态可以修改)
这里点击第二张范爷的图片的文件名
界面如图所示:


五:重命名后的界面如图所示:文件名改成了范爷一枚.jpg了



六:点击每个条目的上传按钮,可以讲它们加入未上传队列,这时候图片将一张张上传到服务器,每一张图片的状态依次改变为:未上传→准备上传→上传中(这是现实进度)→上传完成→已和服务器同步
如图所示:


七:所有的上传队列中图片上传完成后,会弹出确认框,是否保存刚刚上传的这些图片,如果确认,图片的状态就变成了“已和服务器同步”状态了,失败的会变成未上传状态
如图所示:




这里可以看到最后又张图片上传失败了,可以进行重试






前面讲解的都是添加上传的操作,下载讲解删除和下载的操作
说明:所有的删除和取消相关的操作,都是长按条目弹出的
一:删除未上传的图片
长按上面图片的范爷条目,会弹出如下对话框



点击删除按钮后这张图片便被移除了



二:删除已和“已和服务器同步状态”,点击条目,会弹出一样的删除对话框,但是点击删除按钮后,可以选择只从本地删除还是从服务器和本地都删除,如图所示:


如果点击“服务器和本地都删除”则图片会被从本地列表和服务器删除,但是如果点击“只从本地删除”则图片变成“未下载”状态
三:将所有的图片只从本地删除后,界面如图所示


这时候,和上传功能相反,如果点击右侧的下载按钮,图片的状态变化如下:
未下载→准备下载→下载中(显示进度)→下载完成(下次进入附件列表会变成已和服务器同步),如果下载失败会显示“下载失败”并可以重试

四:下载界面如图所示:



五:位于上传下载队列中,但是还没有开始上传和下载的都是可以取消操作的,这里就不截图了
当然,点击本地存在的图片或者已下载完成的图片,是可以发起系统调用查看图片的(实际上任何文件都可以查看)


下面就是核心的代码了:主要是一个Activity:AttachmentActivity,这个就是主界面了,3个帮助类DownLoadUtil:用来下载的帮助类,UploadUtil:用来上传的帮助类,FileIconHelper:用来显示缩略图的帮助类,AttachmentInfo附件实体类,至于其他一些简单常量及没有提到的方法,就不多少了,自行替换就是了。

各个功能点都在代码中有详细的注释:请看代码
AttachmentInfo:
package com.flagcloud.android.mqservice.model.entity;

import java.io.File;
import java.util.Date;
import java.util.HashMap;

import com.flagcloud.android.mqservice.util.CommonHelper;
import com.flagcloud.android.mqservice.util.ConvertUtil;

import android.text.format.Time;

/**
 * 附件信息实体类
 * 
 * @author hdp
 * 
 */
public class AttachmentInfo {
	public static final int NOT_UPLOAD = 1, PREPARE_UPLOAD = 2, UP_LOADING = 3,
			UP_LOADING_SUCCESS = 4, UP_LOADING_FAIL = 5, NOT_DOWNLOAD = 6,
			PREPARE_DOWNLOAD = 7, DOWN_LOADING = 8, DOWN_LOADING_SUCCESS = 9,
			DOWN_LOADING_FAIL = 10, ALREADY_SAVE = 11;
	// 本地有,服务器也已保存,则为已保存状态
	static {
		addToStateMap(1, "未上传");
		addToStateMap(2, "准备上传");
		addToStateMap(3, "上传中:");
		addToStateMap(4, "上传完成");
		addToStateMap(5, "上传失败");
		addToStateMap(6, "未下载");
		addToStateMap(7, "准备下载");
		addToStateMap(8, "下载中:");
		addToStateMap(9, "下载完成");
		addToStateMap(10, "下载失败");
		addToStateMap(11, "已和服务器同步");
	}
	public static HashMap<Integer, String> stateMap;
	// 服务器返回属性
	public String id;
	public String fileName; // 文件名
	public String path; // 文件路径,服务器路径
	public long size; // 文件大小,字节
	public String time;
	// 自定义属性
	public int state; //文件状态
	public int progress = 0; // 操作进度
	public String fileNativePath; // 文件在本地的地址

	public static void addToStateMap(int state, String stateText) {
		if (stateMap == null) {
			stateMap = new HashMap<Integer, String>();
		}
		stateMap.put(state, stateText);
	}

	public AttachmentInfo() {

	}

	public AttachmentInfo(String name, String path, long size,
			String modifyTime, int state, int progress, String fileNativePath) {
		super();
		this.fileName = name;
		this.path = path;
		this.size = size;
		this.time = modifyTime;
		this.state = state;
		this.progress = progress;
		this.fileNativePath = fileNativePath;
	}

	public AttachmentInfo(File file, int state) {
		if (file.exists()) {
			// 如果文件存在
			this.state = state;
			this.fileName = file.getName();
			this.fileNativePath = file.getAbsolutePath();
			this.size = file.length();
			try {
				this.time = ConvertUtil.dateToString(new Date(file
						.lastModified()));
			} catch (Exception e) {
				CommonHelper.log("根据文件信息创建附件转化时间时不正确", e.getMessage());
			}
		}

	}
}



AttachmentActivity:
package com.flagcloud.android.mqservice.biz.attachment;

import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.lang.reflect.Type;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.UUID;

import android.app.Activity;
import android.app.Dialog;
import android.content.ContentValues;
import android.content.Intent;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.preference.PreferenceManager;
import android.provider.MediaStore;
import android.text.TextUtils;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.view.Window;
import android.view.WindowManager;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView.OnItemLongClickListener;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;

import com.flagcloud.android.mqservice.R;
import com.flagcloud.android.mqservice.adapter.AttachmentAdapter;
import com.flagcloud.android.mqservice.async.DownLoadAttachmentListTask;
import com.flagcloud.android.mqservice.async.DownLoadAttachmentListTask.OnDownLoadAtListFinishedListener;
import com.flagcloud.android.mqservice.async.ServiceNoteOperationTask;
import com.flagcloud.android.mqservice.async.ServiceNoteOperationTask.OnServiceNoteOperationFinishedListener;
import com.flagcloud.android.mqservice.biz.attachment.DownLoadUtil.OnDownLoadProcessListener;
import com.flagcloud.android.mqservice.biz.attachment.UploadUtil.OnUploadProcessListener;
import com.flagcloud.android.mqservice.common.Constant;
import com.flagcloud.android.mqservice.common.ResponseObjectUtil;
import com.flagcloud.android.mqservice.model.bindingmodel.ServiceNoteAttachmentsBindingModel;
import com.flagcloud.android.mqservice.model.entity.AttachmentInfo;
import com.flagcloud.android.mqservice.model.entity.ServiceNoteAttachmentInfo;
import com.flagcloud.android.mqservice.model.listitem.OperationListItemViewModel;
import com.flagcloud.android.mqservice.util.CommonHelper;
import com.flagcloud.android.mqservice.util.FileUtil;
import com.flagcloud.android.mqservice.util.GetAbsolutePathUtil;
import com.flagcloud.android.mqservice.util.GsonUtil;
import com.flagcloud.android.mqservice.util.ImageUtil;
import com.flagcloud.android.mqservice.view.dialog.ConfirmDialog;
import com.flagcloud.android.mqservice.view.dialog.InputAndConfirmDialog;
import com.google.gson.reflect.TypeToken;

//附件活动
public class AttachmentActivity extends Activity implements
		OnUploadProcessListener, OnDownLoadProcessListener {

	private static final String TAG = "AttachmentActivity";
	private static final String PHOTO_PATH = FileUtil.sdcardPath
			+ File.separator + FileUtil.APP_FILE_DIRECTOR + File.separator
			+ FileUtil.PHOTO_DIR; // 压缩后的图片存放的目录
	// 自定义上传相关消息
	private static final int TO_UPLOAD_FILE = 1;// 去上传文件
	private static final int UPLOAD_FILE_DONE = 2; // 上传完的响应
	private static final int UPLOAD_IN_PROCESS = 4; // 上传中(更新进度)
	// 自定义下载相关消息
	private static final int TO_DOWNLOAD_FILE = 5; // 去下载文件
	private static final int DOWNLOAD_FILE_DONE = 6; // 下载文件完成
	private static final int DOWNLOAD_IN_PROCESS = 7; // 文件下载中(更新进度)

	private ArrayList<AttachmentInfo> attachmentInfoList = new ArrayList<AttachmentInfo>();
	private SharedPreferences sp;
	private File photoFile;
	public static final int REQUEST_CODE_JOBREPORT = 0x0061,
			REQUEST_CODE_OTHER = 0x0062;
	private Uri photoUri;
	private String subPhotoPath; // 图片目录
	private String serviceNoteId = null;
	private AttachmentInfo currentUpLoadPhotoInfo; // 当前正在上传的对象
	private AttachmentInfo currentDownLoadFileInfo; // 当前正在下载的对象
	private AttachmentAdapter attachmentAdapter;
	private boolean isFirstComeIn; // 是否是首次进入
	private int longClickPosition = -1; // 长按的位置
	private Boolean isUpLoading = false; // 是否有图片正在上传
	private Boolean isDownLoading = false; // 是否有文件正在下载
	private int currentUploadFileTotalSize = 0; // 图片大小加上头部尾部长度
	private long currentDownLoadFileTotalSize = 0; // 当前下载的文件大小

	private Button goBackButton;
	private ListView fileInfoListView;
	private TextView topTitle;
	private InputAndConfirmDialog changeFileNameDialog;
	private Button selectFileButton; // 选择附件的按钮
	private RelativeLayout selectFileRL; // 下部选择文件的整体布局
	private AttachmentInfo selectedNewInfo; // 用户选择的新的附件
	private Button delButton;
	private Button undoButton;
	private Button cancelButton;
	private Dialog menuDialog;
	private Dialog selectImageDialog;
	private ConfirmDialog confirmDialog;
	private Dialog selectDeleteTypeDialog;
	// 下载附件列表的异步任务
	private DownLoadAttachmentListTask downLoadSNList;

	@Override
	public boolean onKeyDown(int keyCode, KeyEvent event) {
		if (keyCode == KeyEvent.KEYCODE_BACK) {
			doGoBack();
		}
		return super.onKeyDown(keyCode, event);
	}

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_up_load_attachment);
		// 创建图片的保存目录
		initializeView();
		addListener();
		sp = PreferenceManager.getDefaultSharedPreferences(this);
		// 将选择的图片保存到应用目录下的photos+任务单id为文件夹的子目录下
		// 进入即创建图片缓存目录,创建photos目录
		boolean success = FileUtil.createSDDir(FileUtil.APP_FILE_DIRECTOR
				+ File.separator + FileUtil.PHOTO_DIR);
		if (!success) {
			Toast.makeText(this, "文件夹创建失败", Toast.LENGTH_SHORT).show();
			return;
		}
		Log.i(TAG, "getAttachmentListFromServer执行了");
		Intent i = getIntent();
		serviceNoteId = i.getStringExtra("serviceNoteId");
		if (TextUtils.isEmpty(serviceNoteId)) {
			Toast.makeText(this, "任务单id有误", Toast.LENGTH_SHORT).show();
			return;
		}
		// 创建子目录
		boolean success2 = FileUtil.createSDDir(FileUtil.APP_FILE_DIRECTOR
				+ File.separator + FileUtil.PHOTO_DIR + File.separator
				+ serviceNoteId);
		if (!success2) {
			Toast.makeText(this, "创建子目录失败", Toast.LENGTH_SHORT).show();
			return;
		}
		// 这里初始化每个任务单对应的目录
		subPhotoPath = FileUtil.sdcardPath + File.separator
				+ FileUtil.APP_FILE_DIRECTOR + File.separator
				+ FileUtil.PHOTO_DIR + File.separator + serviceNoteId;
		Log.i(TAG, "onCreate执行了,当前list大小:" + attachmentInfoList.size());
		// 看是异常进入还是初次进入
		if (savedInstanceState == null) {
			// 初次进入,则从服务器获取
			getAttachmentListFromServer();
			isFirstComeIn = true;
			Log.i(TAG, "savedInstanceState为空");
		} else {
			isFirstComeIn = false;
		}
	}

	// 初始化试图
	public void initializeView() {
		topTitle = (TextView) findViewById(R.id.top_layout_onlygoback_tv_topTitle);
		goBackButton = (Button) findViewById(R.id.top_layout_onlygoback_btn_goBack);
		fileInfoListView = (ListView) findViewById(R.id.attachment_lv_fileInfoList);
		selectFileButton = (Button) findViewById(R.id.attachment_btn_selectFileBtn);
		selectFileRL = (RelativeLayout) findViewById(R.id.attachment_rl_bottomLine);
		topTitle.setText("附件列表");
	}

	// 从服务器获取附件信息的列表
	private void getAttachmentListFromServer() {
		try {
			if (downLoadSNList != null) {
				if (AsyncTask.Status.RUNNING.name().equals(
						downLoadSNList.getStatus().name())
						|| AsyncTask.Status.FINISHED.name().equals(
								downLoadSNList.getStatus().name())) {
					downLoadSNList.cancel(true);
					downLoadSNList = null;
				}
			}
			if (downLoadSNList == null) {
				// 测试实体接口
				// url
				String url = Constant.GET_ATTACHMENTLIST;
				String encoderserviceNoteId = URLEncoder.encode(serviceNoteId,
						"utf-8");
				url += "?id=" + encoderserviceNoteId;
				// 返回对象类型
				Type responseObjectType = new TypeToken<ArrayList<AttachmentInfo>>() {
				}.getType();
				downLoadSNList = new DownLoadAttachmentListTask();
				downLoadSNList.setContext(this);
				downLoadSNList
						.setOnDownLoadAtListFinishedListener(onDownLoadAtListFinishedListener);
				downLoadSNList.execute(new Object[] { url, null, null,
						responseObjectType });
				downLoadSNList.showWaitingDialog(this, new Handler(),
						"正在载入数据...", null);
			}
		} catch (Exception e) {
			CommonHelper.log(TAG + ":doRefresh:", e.getMessage());
		}
	}

	private OnDownLoadAtListFinishedListener onDownLoadAtListFinishedListener = new OnDownLoadAtListFinishedListener() {

		@Override
		public void resultCallBack(Object object) {
			try {
				if (ResponseObjectUtil.ResponseObjectIsNullOrException(
						AttachmentActivity.this, object)) {
					return;
				}
				// 先清空列表
				attachmentInfoList.clear();
				attachmentInfoList = (ArrayList<AttachmentInfo>) object;
				// 和本地目录进行比对,看每个文件在本地是否存在
				Log.i(TAG, "从服务器获取到附近列表大小是" + attachmentInfoList.size());
				compareAllAttachment();
				attachmentAdapter = new AttachmentAdapter(
						AttachmentActivity.this, attachmentInfoList,
						fileInfoListView, operationListener);
				fileInfoListView.setAdapter(attachmentAdapter);
				attachmentAdapter.notifyDataSetChanged();
				fileInfoListView.invalidate();
				Log.i(TAG, "加入本地未上传文件后的大小是:" + attachmentInfoList.size());
			} catch (Exception e) {
				CommonHelper.log(TAG + ":onDownLoadSNListFinishedListener",
						e.getMessage());
			}
		}
	};

	// 比对所有的文件
	private void compareAllAttachment() {
		File FileDir = new File(PHOTO_PATH + File.separator + serviceNoteId);
		if (FileDir.exists() && FileDir.isDirectory()) {
			// 根据当前附件列表遍历本地文件
			for (int j = 0; j < attachmentInfoList.size(); j++) {
				AttachmentInfo info = attachmentInfoList.get(j);
				// 服务器附件默认状态设置为未下载
				info.state = AttachmentInfo.NOT_DOWNLOAD;
				File[] allFiles = FileDir.listFiles();
				// 在本地有,在服务器没有的图片文件应该显示未上传
				if (allFiles != null) {
					for (int i = 0; i < allFiles.length; i++) {
						File tempFile = allFiles[i];
						if (info.fileName != null
								&& info.fileName.equals(tempFile.getName())) {
							// 如果有同名文件,则给本地地址赋值
							info.fileNativePath = tempFile.getAbsolutePath();
							info.state = AttachmentInfo.ALREADY_SAVE;
						}
					}
				}
			}
			// 根据本地文件遍历附件列表,如果没有,则加入并显示未上传
			ArrayList<AttachmentInfo> tempList = new ArrayList<AttachmentInfo>();
			File[] allFiles = FileDir.listFiles();
			if (allFiles != null) {
				for (int i = 0; i < allFiles.length; i++) {
					File tempFile = allFiles[i];
					boolean isAlreadyUpload = false;
					for (int j = 0; j < attachmentInfoList.size(); j++) {
						AttachmentInfo info = attachmentInfoList.get(j);
						String fileName = info.fileName;
						if (!TextUtils.isEmpty(fileName)) {
							if (fileName.equals(tempFile.getName())) {
								isAlreadyUpload = true;
							}
						}
					}
					if (!isAlreadyUpload) {
						// 本地有服务器没有的,如果是图片,加入列表,并显示未上传
						tempList.add(new AttachmentInfo(tempFile,
								AttachmentInfo.NOT_UPLOAD));
					}
				}
			}
			// 更新总的列表
			attachmentInfoList.addAll(tempList);
		}
	}

	private void addListener() {
		goBackButton.setOnClickListener(onClickListener);
		fileInfoListView.setOnItemClickListener(onFileItemClickListener);
		// 长按弹出optionsMenu
		fileInfoListView.setOnItemLongClickListener(itemLongClickListener);
		selectFileButton.setOnClickListener(onClickListener);
	}

	// 长按条目的监听
	private OnItemLongClickListener itemLongClickListener = new OnItemLongClickListener() {

		@Override
		public boolean onItemLongClick(AdapterView<?> parent, View view,
				int position, long id) {
			// 打开菜单
			longClickPosition = position;
			showMenuDialog();
			return false;
		}
	};

	// 根据图片的状态,显示不同的长按菜单
	private void showMenuDialog() {
		try {
			View view = getLayoutInflater().inflate(
					R.layout.upload_file_menu_dialog, null);
			// 删除操作
			delButton = (Button) view
					.findViewById(R.id.attachment_dialog_btn_button1);
			// 取消操作
			undoButton = (Button) view
					.findViewById(R.id.attachment_dialog_btn_button2);
			// 取消按钮
			cancelButton = (Button) view
					.findViewById(R.id.attachment_dialog_btn_cancel);
			cancelButton.setOnClickListener(menuDialogItemListener);
			delButton.setOnClickListener(menuDialogItemListener);
			undoButton.setOnClickListener(menuDialogItemListener);
			menuDialog = new Dialog(this, R.style.transparentFrameWindowStyle);
			menuDialog.setContentView(view, new LayoutParams(
					LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT));
			Window window = menuDialog.getWindow();
			// 设置显示动画
			window.setWindowAnimations(R.style.main_menu_animstyle);
			WindowManager.LayoutParams wl = window.getAttributes();
			wl.x = 0;
			wl.y = getWindowManager().getDefaultDisplay().getHeight();
			// 以下这两句是为了保证按钮可以水平满屏
			wl.width = ViewGroup.LayoutParams.MATCH_PARENT;
			wl.height = ViewGroup.LayoutParams.WRAP_CONTENT;
			// 设置显示位置
			menuDialog.onWindowAttributesChanged(wl);
			// 设置点击外围解散
			menuDialog.setCanceledOnTouchOutside(true);
			menuDialog.show();
			// 根据不同的装填,显示不同的提示
			if (longClickPosition >= 0) {
				AttachmentInfo info = attachmentInfoList.get(longClickPosition);
				int state = info.state;
				if (state == AttachmentInfo.UP_LOADING) {
					undoButton.setText("取消上传");
					undoButton.setEnabled(false);// 暂时禁用取消正在上传的操作
					// 禁用掉删除按钮
					delButton.setEnabled(false);
				} else if (state == AttachmentInfo.PREPARE_UPLOAD) {
					// 准备上传
					undoButton.setText("取消上传");
				} else if (state == AttachmentInfo.NOT_UPLOAD) {
					// 未上传,
					undoButton.setText("取消上传");
					undoButton.setEnabled(false);
				} else if (state == AttachmentInfo.NOT_DOWNLOAD) {
					// 未下载
					undoButton.setText("取消下载");
					undoButton.setEnabled(false);
				} else if (state == AttachmentInfo.PREPARE_DOWNLOAD) {
					undoButton.setText("取消下载");
				} else if (state == AttachmentInfo.DOWN_LOADING) {
					undoButton.setText("取消下载");
					delButton.setEnabled(false);// 暂时禁用取消正在下载的操作
				} else {
					undoButton.setEnabled(false);
				}
			}
		} catch (Exception e) {
			CommonHelper.log(TAG + ":showMenuDialog", e.getMessage());
		}
	}

	private View.OnClickListener menuDialogItemListener = new OnClickListener() {

		@Override
		public void onClick(View v) {
			if (menuDialog != null) {
				menuDialog.dismiss();
				menuDialog = null;
			}
			if (v == delButton) {
				// 删除文件操作
				deleteLongClickItem();
			} else if (v == undoButton) {
				// 取消上传
				doMenuTwoAction();
			}
		}
	};

	// 第二个菜单的响应事件,第二个按钮只做取消操作
	private void doMenuTwoAction() {
		if (longClickPosition >= 0) {
			AttachmentInfo info = attachmentInfoList.get(longClickPosition);
			int state = info.state;
			// 如果是取消上传菜单,将状态置为为上传
			if (state == AttachmentInfo.NOT_UPLOAD) {
				// 未上传
				// info.state = AttachmentInfo.PREPARE_UPLOAD;
			} else if (state == AttachmentInfo.PREPARE_UPLOAD) {
				// 准备上传
				info.state = AttachmentInfo.NOT_UPLOAD;
			} else if (state == AttachmentInfo.UP_LOADING) {
				// 上传中
			} else if (state == AttachmentInfo.NOT_DOWNLOAD) {
				// 未下载
			} else if (state == AttachmentInfo.PREPARE_DOWNLOAD) {
				// 准备下载
				info.state = AttachmentInfo.NOT_DOWNLOAD;
			} else if (state == AttachmentInfo.DOWN_LOADING) {
				// 下载中
				info.state = AttachmentInfo.NOT_DOWNLOAD;
			} else {
				// 其他状态设置成未上传
				info.state = AttachmentInfo.NOT_UPLOAD;
			}
			attachmentAdapter.notifyDataSetChanged();
			fileInfoListView.invalidate();
		}
	}

	// 点击图片的监听
	private OnItemClickListener onFileItemClickListener = new OnItemClickListener() {

		@Override
		public void onItemClick(AdapterView<?> parent, View view, int position,
				long id) {
			AttachmentInfo info = attachmentInfoList.get(position);
			showAttachment(info);
		}
	};

	// 点击条目后显示详情的视图
	private void showAttachment(AttachmentInfo info) {
		try {
			int state = info.state;
			// 只有是已下载完成或者所有上传相关的状态,才能够查看详情
			if (state == AttachmentInfo.NOT_UPLOAD
					|| state == AttachmentInfo.PREPARE_UPLOAD
					|| state == AttachmentInfo.UP_LOADING
					|| state == AttachmentInfo.UP_LOADING_SUCCESS
					|| state == AttachmentInfo.UP_LOADING_FAIL
					|| state == AttachmentInfo.DOWN_LOADING_SUCCESS
					|| state == AttachmentInfo.ALREADY_SAVE) {
				// 发出查看文件的意图
				com.flagcloud.android.mqservice.common.IntentBuilder.viewFile(
						this, info.fileNativePath);
			}
		} catch (Exception e) {
			CommonHelper.log(TAG + ":showAttachment", e.getMessage());
		}

	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// 删除
		menu.add(0, Constant.Delete_ITEMID, 0, "删除");
		return super.onCreateOptionsMenu(menu);
	}

	// 从服务器删除长按条目
	private void doDeleteAttachmentFromServer() {
		try {
			Log.i(TAG, "进入了从服务器删除照片的方法");
			AttachmentInfo longClickInfo = attachmentInfoList
					.get(longClickPosition);
			String attachId = longClickInfo.id;
			String encoderId = URLEncoder.encode(attachId, "utf-8");
			// url
			String url = Constant.DELETE_ATTACHMENT_ITEM + "?id=" + encoderId;
			// 返回对象类型
			Type responseObjectType = OperationListItemViewModel.class;
			ServiceNoteOperationTask serviceNoteOperationTask = new ServiceNoteOperationTask();
			serviceNoteOperationTask.setContext(this);
			serviceNoteOperationTask
					.setOnServiceNoteOperationFinishedListener(deleteAttamentItemFinishedListener);
			serviceNoteOperationTask.showWaitingDialog(this, new Handler(),
					"正在删除所选附件...", null);
			serviceNoteOperationTask.execute(new Object[] { url, null, null,
					responseObjectType });

		} catch (Exception e) {
			CommonHelper.log(TAG + ":doDeleteAttachmentFromServer",
					e.getMessage());
		}
	}

	private OnServiceNoteOperationFinishedListener deleteAttamentItemFinishedListener = new OnServiceNoteOperationFinishedListener() {

		@Override
		public void resultCallBack(Object object) {
			if (ResponseObjectUtil.ResponseObjectIsNullOrException(
					AttachmentActivity.this, object)) {
				Toast.makeText(AttachmentActivity.this, "附件删除失败",
						Toast.LENGTH_SHORT).show();
				return;
			}
			// 删除成功
			Toast.makeText(AttachmentActivity.this, "附件删除成功!",
					Toast.LENGTH_SHORT).show();
			// 本地是否存在,如果存在,则本地文件也删除
			try {
				AttachmentInfo info = attachmentInfoList.get(longClickPosition);
				File tempFile = new File(info.fileNativePath);
				if (tempFile != null && tempFile.exists()) {
					tempFile.delete();
				}
				// 从视图列表中移除
				attachmentInfoList.remove(longClickPosition);
				// 更新视图
				attachmentAdapter.notifyDataSetChanged();
				fileInfoListView.invalidate();
			} catch (Exception e) {
				CommonHelper.log(TAG + ":deleteAttamentItemFinishedListener",
						e.getMessage());
			}
		}

	};

	private void showDeleteFromServerConfirmDialog() {
		try {
			Log.i(TAG, "进入了从服务器删除的确认对话框");
			final ConfirmDialog deleteConfirmDialog = new ConfirmDialog(
					AttachmentActivity.this, R.style.serviceNoteOperationDialog);
			deleteConfirmDialog.setAgreeListener(new View.OnClickListener() {

				@Override
				public void onClick(View v) {
					if (deleteConfirmDialog != null) {
						deleteConfirmDialog.dismiss();
					}
					doDeleteAttachmentFromServer();
				}
			});
			deleteConfirmDialog.setDeclineListener(new View.OnClickListener() {

				@Override
				public void onClick(View v) {
					if (deleteConfirmDialog != null) {
						deleteConfirmDialog.dismiss();
					}
				}
			});
			deleteConfirmDialog.show();
			deleteConfirmDialog.setTvTitle("删除附件");
			deleteConfirmDialog.setTvContent("确认从服务器删除吗?");
		} catch (Exception e) {
			CommonHelper.log(TAG + ":showDeleteFromServerConfirmDialog",
					e.getMessage());
		}
	}

	// 已上传至服务器的附件,提示是从本地删除还是从服务器删除
	private void showSelectDeleteTypeDialog() {
		try {
			// 进入选择删除类型的对话框
			Log.i(TAG, "进入了选择删除类型的对话框");
			View view = getLayoutInflater().inflate(
					R.layout.upload_file_menu_dialog, null);
			// 只从本地删除按钮
			Button nativeDelButton = (Button) view
					.findViewById(R.id.attachment_dialog_btn_button1);
			// 本地和服务器都删除按钮
			Button bothDelButton = (Button) view
					.findViewById(R.id.attachment_dialog_btn_button2);
			// 取消按钮
			Button cancelButton = (Button) view
					.findViewById(R.id.attachment_dialog_btn_cancel);
			cancelButton.setOnClickListener(selectDeleteTypeItemClickListener);
			nativeDelButton
					.setOnClickListener(selectDeleteTypeItemClickListener);
			bothDelButton.setOnClickListener(selectDeleteTypeItemClickListener);
			selectDeleteTypeDialog = new Dialog(this,
					R.style.transparentFrameWindowStyle);
			selectDeleteTypeDialog.setContentView(view, new LayoutParams(
					LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT));
			Window window = selectDeleteTypeDialog.getWindow();
			// 设置显示动画
			window.setWindowAnimations(R.style.main_menu_animstyle);
			WindowManager.LayoutParams wl = window.getAttributes();
			wl.x = 0;
			wl.y = getWindowManager().getDefaultDisplay().getHeight();
			// 以下这两句是为了保证按钮可以水平满屏
			wl.width = ViewGroup.LayoutParams.MATCH_PARENT;
			wl.height = ViewGroup.LayoutParams.WRAP_CONTENT;
			// 设置显示位置
			selectDeleteTypeDialog.onWindowAttributesChanged(wl);
			// 设置点击外围解散
			selectDeleteTypeDialog.setCanceledOnTouchOutside(true);
			selectDeleteTypeDialog.show();
			// 根据不同的装填,显示不同的提示
			nativeDelButton.setText("只从本地删除");
			bothDelButton.setText("服务器和本地都删除");
		} catch (Exception e) {
			CommonHelper.log(TAG + ":showSelectDeleteTypeDialog",
					e.getMessage());
		}
	}

	// 只从本地删除
	private void doDeleteAttachmentOnlyFromNative() {
		try {
			AttachmentInfo info = attachmentInfoList.get(longClickPosition);
			File tempFile = new File(info.fileNativePath);
			if (tempFile != null && tempFile.exists()) {
				// 存在,则从本地删除
				if (tempFile.delete()) {
					// 如果删除成功,则将状态设置成未下载
					info.state = AttachmentInfo.NOT_DOWNLOAD;
					attachmentAdapter.notifyDataSetChanged();
					fileInfoListView.invalidate();
				} else {
					Toast.makeText(this, "本地文件删除失败", Toast.LENGTH_SHORT).show();
				}
			}
		} catch (Exception e) {
			CommonHelper.log(TAG + ":doDeleteOnlayFromNative", e.getMessage());
		}
	}

	private View.OnClickListener selectDeleteTypeItemClickListener = new OnClickListener() {

		@Override
		public void onClick(View v) {
			if (selectDeleteTypeDialog != null) {
				selectDeleteTypeDialog.dismiss();
				selectDeleteTypeDialog = null;
			}
			int id = v.getId();
			if (id == R.id.attachment_dialog_btn_button1) {
				// 只从本地删除
				doDeleteAttachmentOnlyFromNative();
			} else if (id == R.id.attachment_dialog_btn_button2) {
				// 从服务器和本地都删除
				showDeleteFromServerConfirmDialog();
			} else if (id == R.id.attachment_dialog_btn_cancel) {
				// 取消
			}
		}
	};

	// 删除长按的条目
	public void deleteLongClickItem() {
		try {
			if (longClickPosition >= 0) {
				if (attachmentInfoList.size() - 1 >= longClickPosition) {
					// 根据任务单的状态,提示是只从本地删除还是从服务器删除
					// 从文件夹下移除,从列表中移除
					AttachmentInfo info = attachmentInfoList
							.get(longClickPosition);
					int state = info.state;
					if (state == AttachmentInfo.NOT_DOWNLOAD) {
						// 未下载,提示是否从服务器删除
						showDeleteFromServerConfirmDialog();
					} else if (state == AttachmentInfo.ALREADY_SAVE
							|| state == AttachmentInfo.DOWN_LOADING_SUCCESS) {
						// 已保存至服务器或者下载完成状态,提示是从本地删除还是从服务器删除
						showSelectDeleteTypeDialog();
					} else {
						// 其他状态,直接删除本地,如果本地有
						File tempFile = new File(info.fileNativePath);
						if (tempFile.exists()) {
							tempFile.delete();
						}
						attachmentInfoList.remove(longClickPosition);
						attachmentAdapter.notifyDataSetChanged();
						fileInfoListView.invalidate();
					}
				}
			}
		} catch (Exception e) {
			CommonHelper.log(TAG + ":deleteLongClickItem", e.getMessage());
		}
	}

	private OnClickListener onClickListener = new OnClickListener() {

		@Override
		public void onClick(View v) {
			if (v == goBackButton) {
				doGoBack();
			} else if (v == selectFileButton) {
				// 发送一个要上传图片的消息
				if (selectImageDialog != null) {
					if (!selectImageDialog.isShowing()) {
						showSelectImageDialog();
					}
				} else {
					showSelectImageDialog();
				}
			}

		}
	};

	private void showSelectImageDialog() {
		try {
			View view = getLayoutInflater().inflate(
					R.layout.upload_file_menu_dialog, null);
			// 图库
			Button selectImageButton = (Button) view
					.findViewById(R.id.attachment_dialog_btn_button1);
			// 相机
			Button cameraButton = (Button) view
					.findViewById(R.id.attachment_dialog_btn_button2);
			// 取消按钮
			Button cancelButton = (Button) view
					.findViewById(R.id.attachment_dialog_btn_cancel);
			cancelButton.setOnClickListener(selectPhotoListener);
			selectImageButton.setOnClickListener(selectPhotoListener);
			cameraButton.setOnClickListener(selectPhotoListener);
			selectImageDialog = new Dialog(this,
					R.style.transparentFrameWindowStyle);
			selectImageDialog.setContentView(view, new LayoutParams(
					LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT));
			Window window = selectImageDialog.getWindow();
			// 设置显示动画
			window.setWindowAnimations(R.style.main_menu_animstyle);
			WindowManager.LayoutParams wl = window.getAttributes();
			wl.x = 0;
			wl.y = getWindowManager().getDefaultDisplay().getHeight();
			// 以下这两句是为了保证按钮可以水平满屏
			wl.width = ViewGroup.LayoutParams.MATCH_PARENT;
			wl.height = ViewGroup.LayoutParams.WRAP_CONTENT;
			// 设置显示位置
			selectImageDialog.onWindowAttributesChanged(wl);
			// 设置点击外围解散
			selectImageDialog.setCanceledOnTouchOutside(true);
			selectImageDialog.show();
			selectImageButton.setText("图库");
			cameraButton.setText("相机");
		} catch (Exception e) {
			CommonHelper.log(TAG + ":showMenuDialog", e.getMessage());
		}
	}

	private View.OnClickListener selectPhotoListener = new OnClickListener() {

		@Override
		public void onClick(View v) {
			if (selectImageDialog != null) {
				selectImageDialog.dismiss();
				selectImageDialog = null;
			}
			int id = v.getId();
			if (id == R.id.attachment_dialog_btn_button1) {
				// 相册
				Intent i = new Intent(
						Intent.ACTION_PICK,
						android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
				startActivityForResult(i, Constant.SELECT_PHOTO_CODE);

			} else if (id == R.id.attachment_dialog_btn_button2) {
				// 直接保存到相册
				Intent intentCamera = new Intent(
						MediaStore.ACTION_IMAGE_CAPTURE);
				ContentValues values = new ContentValues();
				photoUri = AttachmentActivity.this.getContentResolver().insert(
						MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
				intentCamera.putExtra(android.provider.MediaStore.EXTRA_OUTPUT,
						photoUri);
				startActivityForResult(intentCamera, Constant.CHECK_PHOTO_CODE);
			}

		}
	};

	// 开始上传
	public void doUpLoadFile(AttachmentInfo info) {
		try {
			currentUpLoadPhotoInfo = info;
			String fileKey = "file";
			UploadUtil uploadUtil = UploadUtil.getInstance();
			uploadUtil.setCancelFlag(false);
			uploadUtil.setOnUploadProcessListener(this); // 设置监听器监听上传状态
			uploadUtil.uploadFile(info.fileNativePath, fileKey,
					Constant.UPLOAD_PHOTO);
			currentUpLoadPhotoInfo.state = AttachmentInfo.UP_LOADING;
			attachmentAdapter.notifyDataSetChanged();
			fileInfoListView.invalidate();
		} catch (Exception e) {
			CommonHelper.log(TAG + ":doUpLoad", e.getMessage());
		}
	}

	public void doGoBack() {
		try {
			// 判断是否有正在下载或上传的任务
			if (isDownLoading || isUpLoading) {
				// 提示当前正在有任务正在下载或上传,退出将取消正在进行的任务
				final ConfirmDialog backConfirmDialog = new ConfirmDialog(
						AttachmentActivity.this,
						R.style.serviceNoteOperationDialog);
				// 确认新建
				backConfirmDialog.setAgreeListener(new View.OnClickListener() {

					@Override
					public void onClick(View v) {
						UploadUtil.getInstance().setCancelFlag(true);
						DownLoadUtil.getInstance().setCancelFlag(true);
						AttachmentActivity.this.finish();
						overridePendingTransition(R.anim.no_anim,
								R.anim.slide_out_right);
						if (backConfirmDialog != null) {
							backConfirmDialog.dismiss();
						}
					}
				});
				// 不新建
				backConfirmDialog
						.setDeclineListener(new View.OnClickListener() {

							@Override
							public void onClick(View v) {
								if (backConfirmDialog != null) {
									backConfirmDialog.dismiss();
								}
							}
						});
				backConfirmDialog.show();
				backConfirmDialog.setTvTitle("退出确认");
				backConfirmDialog.setTvContent("当前有任务进行,确认退出吗?");
			} else {
				AttachmentActivity.this.finish();
				overridePendingTransition(R.anim.no_anim,
						R.anim.slide_out_right);
			}
		} catch (Exception e) {
			CommonHelper.log(TAG + ":goBack", e.getMessage());
		}
	}

	@Override
	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
		switch (requestCode) {
		case Constant.SELECT_PHOTO_CODE:
			selectToAddPhoto(resultCode, data);
			break;
		case Constant.CHECK_PHOTO_CODE:
			addAttachementByTakePhoto(resultCode, data);
			break;
		case REQUEST_CODE_JOBREPORT:
			if (resultCode == RESULT_OK) {
				this.finish();
			}
			break;
		default:
			break;
		}
		Log.i(TAG, "onActivityResult执行完,当期list大小是:" + attachmentInfoList.size());
		super.onActivityResult(requestCode, resultCode, data);
	}

	// 通过拍照的方式添加附件(图片)
	private void addAttachementByTakePhoto(int resultCode, Intent data) {
		if (resultCode == RESULT_OK) {
			//
			String[] pojo = { MediaStore.Images.Media.DATA };
			Cursor cursor = managedQuery(photoUri, pojo, null, null, null);
			String picPath = null;
			if (cursor != null) {
				int columnIndex = cursor.getColumnIndexOrThrow(pojo[0]);
				cursor.moveToFirst();
				picPath = cursor.getString(columnIndex);
				try {
					// 4.0以上的版本会自动关闭 (4.0--14;; 4.0.3--15)
					if (Integer.parseInt(Build.VERSION.SDK) < 14) {
						// 只有4.0以下才需要手动关闭
						cursor.close();
					}
				} catch (Exception e) {
					CommonHelper.log(TAG + ":addPhoto", e.getMessage());
				}
			}
			Log.i(TAG, "imagePath = " + picPath);
			if (ImageUtil.isImage(picPath)) {
				// change image size
				try {
					Bitmap newFile = CommonHelper.getSmallBitmap(picPath);
					photoFile = new File(PHOTO_PATH + File.separator
							+ serviceNoteId, UUID.randomUUID().toString()
							.replace("-", "")
							+ Constant.ATTACH_TYPE_IMAGE_POSTFIX_JPEG);
					OutputStream os = new FileOutputStream(photoFile);
					newFile.compress(CompressFormat.JPEG, 98, os);
					os.flush();
					os.close();
					newFile.recycle();
					// 将复制到自己目录的图片文件添加到列表
					selectedNewInfo = new AttachmentInfo(photoFile, 1);
				} catch (Exception e) {
					CommonHelper.log(this.getClass().getName(),
							"addPhoto->zoom:" + e.getMessage());
				}
			} else {
				Toast.makeText(this, "选择图片文件不正确", Toast.LENGTH_LONG).show();
			}
		}
	}

	// 通过相册选择附件(图片)
	private void selectToAddPhoto(int arg1, Intent arg2) {
		if (arg2 != null && arg2.getData() != null) {
			String imagePath = GetAbsolutePathUtil.getAbsolutePathFromURI(
					arg2.getData(), this);
			if (ImageUtil.isImage(imagePath)) {
				FileUtil util = new FileUtil(getBaseContext());
				String fileName = UUID.randomUUID().toString().replace("-", "")
						+ Constant.ATTACH_TYPE_IMAGE_POSTFIX_JPEG;
				photoFile = new File(PHOTO_PATH + File.separator
						+ serviceNoteId, fileName);
				// 复制到自己的缓存目录
				util.copyFileFromTo(imagePath, PHOTO_PATH + File.separator
						+ serviceNoteId, fileName);
				// change image size
				try {
					// 将图片压缩并保存到photoFile
					Bitmap newFile = CommonHelper.getSmallBitmap(photoFile
							.getAbsolutePath());
					OutputStream os = new FileOutputStream(photoFile);
					newFile.compress(CompressFormat.JPEG, 98, os);
					os.flush();
					os.close();
					newFile.recycle();
					selectedNewInfo = new AttachmentInfo(photoFile, 1);
				} catch (Exception e) {
					CommonHelper.log(this.getClass().getName(),
							"addPhoto->zoom:" + e.getMessage());
				}
			} else {
				Toast.makeText(
						AttachmentActivity.this,
						getResources()
								.getString(R.string.wrong_picture_or_path),
						Toast.LENGTH_LONG).show();
			}
		}
	}

	@Override
	protected void onSaveInstanceState(Bundle outState) {
		Log.i(TAG, "onSaveInstanceState执行了,当期那listview大小是:"
				+ attachmentInfoList.size());
		super.onSaveInstanceState(outState);
	}

	@Override
	protected void onRestoreInstanceState(Bundle savedInstanceState) {
		Log.i(TAG, "onSaveInstanceState执行了,当期那listview大小是:"
				+ attachmentInfoList.size());
		super.onRestoreInstanceState(savedInstanceState);
	}

	@Override
	protected void onResume() {
		try {
			if (!isFirstComeIn) {
				// 不是首次进入,则从sp文件中恢复数据
				attachmentInfoList.clear();
				// 和每个任务单的id绑定
				String json = sp.getString(serviceNoteId + "attachment", null);
				if (json != null) {
					attachmentInfoList = GsonUtil.GetGsonObj().fromJson(json,
							new TypeToken<ArrayList<AttachmentInfo>>() {
							}.getType());
				}
				// 如果是恢复,则每次将listview滚动到底部
				fileInfoListView.setSelection(attachmentAdapter.getCount() - 1);
				// 恢复保存的子文件夹目录
				subPhotoPath = sp.getString("subPhotoPath", "");
			}
			if (selectedNewInfo != null) {
				attachmentInfoList.add(selectedNewInfo);
				selectedNewInfo = null;
			}
			attachmentAdapter = new AttachmentAdapter(this, attachmentInfoList,
					fileInfoListView, operationListener);
			fileInfoListView.setAdapter(attachmentAdapter);
			attachmentAdapter.notifyDataSetChanged();
			fileInfoListView.invalidate();
			// 恢复之后将sp中保存的数据清空
			sp.edit().putString(serviceNoteId + "attachment", "").commit();
			Log.i(TAG, "onResume执行了,当前list大小:" + attachmentInfoList.size());
		} catch (Exception e) {
			CommonHelper.log(TAG + ":onRestoreInstanceState", e.getMessage());
		}
		super.onResume();
	}

	@Override
	protected void onPause() {
		try {
			// 保存列表数据到sp文件中
			String jsonStr = GsonUtil.GetGsonObj().toJson(attachmentInfoList,
					new TypeToken<ArrayList<AttachmentInfo>>() {
					}.getType());
			sp.edit().putString(serviceNoteId + "attachment", jsonStr).commit();
			Log.i(TAG, "onPause执行了,当前list大小:" + attachmentInfoList.size());
			// 保存子文件夹目录
			sp.edit().putString("subPhotoPath", subPhotoPath).commit();
		} catch (Exception e) {
			CommonHelper.log(TAG + ":onSaveInstanceState", e.getMessage());
		}
		super.onPause();
	}

	// 上传,取消,下载,取消,删除等操作的监听
	private OnClickListener operationListener = new OnClickListener() {

		@Override
		public void onClick(View v) {
			int id = v.getId();
			if (id == R.id.file_list_item_fileName) {
				// 点击了文件名
				AttachmentInfo tag = (AttachmentInfo) v.getTag();
				int state = tag.state;
				if (state == AttachmentInfo.UP_LOADING
						|| state == AttachmentInfo.DOWN_LOADING
						|| state == AttachmentInfo.ALREADY_SAVE
						|| state == AttachmentInfo.NOT_DOWNLOAD
						|| state == AttachmentInfo.DOWN_LOADING_SUCCESS
						|| state == AttachmentInfo.DOWN_LOADING_FAIL
						|| state == AttachmentInfo.UP_LOADING_FAIL
						|| state == AttachmentInfo.UP_LOADING_SUCCESS) {
					// 正在上传,正在下载,已和服务器同步状态,未下载,不允许重命名
					return;
				}
				showChangeFileNameDialog(tag);
			} else if ((id == R.id.file_list_item_upordownLoad)
					|| (id == R.id.file_list_item_operationFrameLayout)) {
				// 点击了操作图标,或者整个操作布局
				// 获取绑定的对象
				AttachmentInfo tag = (AttachmentInfo) v.getTag();
				// 上传相关只有未上传和上传失败才显示操作按钮
				int state = tag.state;
				switch (state) {
				// 按钮主要出发积极操作
				case AttachmentInfo.NOT_UPLOAD:
					// 未上传,点击表示准备上传,加入上传队列
					tag.state = AttachmentInfo.PREPARE_UPLOAD;
					// 发送一个上传文件的消息
					handler.sendEmptyMessage(TO_UPLOAD_FILE);
					break;
				case AttachmentInfo.UP_LOADING_FAIL:
					// 上传失败,点击表示重新上传
					tag.state = AttachmentInfo.PREPARE_UPLOAD;
					handler.sendEmptyMessage(TO_UPLOAD_FILE);
					break;
				case AttachmentInfo.NOT_DOWNLOAD:
					// 未下载
					tag.state = AttachmentInfo.PREPARE_DOWNLOAD;
					handler.sendEmptyMessage(TO_DOWNLOAD_FILE);
					break;
				case AttachmentInfo.DOWN_LOADING_FAIL:
					// 下载失败
					tag.state = AttachmentInfo.PREPARE_DOWNLOAD;
					handler.sendEmptyMessage(TO_DOWNLOAD_FILE);
					break;
				default:
					break;
				}
			}
			attachmentAdapter.notifyDataSetChanged();
			fileInfoListView.invalidate();
		}
	};

	// 显示给图片重命名的对话框
	public void showChangeFileNameDialog(final AttachmentInfo attachmentInfo) {
		try {
			if (changeFileNameDialog == null) {
				changeFileNameDialog = new InputAndConfirmDialog(this,
						R.style.serviceNoteOperationDialog);
				changeFileNameDialog
						.setAgreeListener(new View.OnClickListener() {

							@Override
							public void onClick(View v) {
								String inputText = changeFileNameDialog
										.getInputText();
								doChangeFileName(attachmentInfo, inputText);
								if (changeFileNameDialog != null) {
									changeFileNameDialog.dismiss();
									changeFileNameDialog = null;
								}
							}
						});
				changeFileNameDialog
						.setDeclineListener(new View.OnClickListener() {

							@Override
							public void onClick(View v) {
								if (changeFileNameDialog != null) {
									changeFileNameDialog.dismiss();
									changeFileNameDialog = null;
								}
							}
						});
				changeFileNameDialog.show();
				changeFileNameDialog
						.setOriginalInputText(attachmentInfo.fileName);
				changeFileNameDialog.setInputPrompt("请输入新文件名");
				changeFileNameDialog.setTitle("文件重命名");
			}
			changeFileNameDialog.show();

		} catch (Exception e) {
			CommonHelper.log(TAG + ":clickAddCommnets", e.getMessage());
		}
	}

	// 重命名后保存操作的对话框
	private void doChangeFileName(AttachmentInfo info, String newName) {
		try {
			File oldFile = new File(info.fileNativePath);
			if (oldFile.exists()) {
				File newFile = new File(PHOTO_PATH + File.separator
						+ serviceNoteId + File.separator + newName);
				if (oldFile.renameTo(newFile)) {
					// 重命名成功的话
					oldFile.delete();
					info.fileName = newName;
					info.fileNativePath = newFile.getAbsolutePath();
					attachmentAdapter.notifyDataSetChanged();
					fileInfoListView.invalidate();
					// 这里应该发一个重命名的请求
				} else {
					Toast.makeText(this, "重命名失败", Toast.LENGTH_SHORT).show();
				}

			}

		} catch (Exception e) {
			CommonHelper.log(TAG + ":addPhotoDesc", e.getMessage());
		}

	}

	private ArrayList<ServiceNoteAttachmentInfo> getAttachmentInfos() {
		ArrayList<ServiceNoteAttachmentInfo> attachments = new ArrayList<ServiceNoteAttachmentInfo>();
		for (AttachmentInfo info : attachmentInfoList) {
			// 如果状态为已完成,则添加到保存列表
			if (AttachmentInfo.UP_LOADING_SUCCESS == info.state) {
				ServiceNoteAttachmentInfo snatInfo = new ServiceNoteAttachmentInfo();
				snatInfo.fileName = info.fileName;
				snatInfo.path = info.path;// 只有保存的时候用到的是path,其他是本地地址
				// 描述赞为空
				snatInfo.description = "";
				attachments.add(snatInfo);
			}
		}
		return attachments;
	}

	// 保存所有已上传完成附件的操作
	private void doSaveAllUploadAttachments() {
		try {
			if (confirmDialog != null) {
				confirmDialog.dismiss();
			}
			ServiceNoteAttachmentsBindingModel model = new ServiceNoteAttachmentsBindingModel();
			model.serviceNoteId = this.serviceNoteId;
			model.attachments = getAttachmentInfos();
			// url
			String url = Constant.SAVE_ATTACHMENTLIST;
			// 请求对象
			Object requestObject = model;
			// 请求对象类型
			Type requestObjectType = ServiceNoteAttachmentsBindingModel.class;
			// 返回对象类型
			Type responseObjectType = OperationListItemViewModel.class;
			ServiceNoteOperationTask serviceNoteOperationTask = new ServiceNoteOperationTask();
			serviceNoteOperationTask.setContext(this);
			serviceNoteOperationTask
					.setOnServiceNoteOperationFinishedListener(saveAttamentsFinishedListener);
			serviceNoteOperationTask.showWaitingDialog(this, new Handler(),
					"正在保存上传附件", null);
			serviceNoteOperationTask.execute(new Object[] { url, requestObject,
					requestObjectType, responseObjectType });
		} catch (Exception e) {
			CommonHelper
					.log(TAG + "doSaveAlluploadAttachemnts", e.getMessage());
		}
	}

	private OnServiceNoteOperationFinishedListener saveAttamentsFinishedListener = new OnServiceNoteOperationFinishedListener() {

		@Override
		public void resultCallBack(Object object) {
			if (ResponseObjectUtil.ResponseObjectIsNullOrException(
					AttachmentActivity.this, object)) {
				if (confirmDialog != null) {
					// 保存失败,则重试
					confirmDialog.show();
				}
				return;
			}
			// 保存成功后刷新整个列表
			getAttachmentListFromServer();
		}
	};

	// 不保存上传成功的所有附件
	public void doNoteSaveAllUploadAttachments() {
		try {
			if (confirmDialog != null) {
				confirmDialog.dismiss();
			}
			ServiceNoteAttachmentsBindingModel model = new ServiceNoteAttachmentsBindingModel();
			model.serviceNoteId = this.serviceNoteId;
			model.attachments = getAttachmentInfos();
			// url
			String url = Constant.CANCEL_ATTACHMENT_LIST;
			// 请求对象
			Object requestObject = model;
			// 请求对象类型
			Type requestObjectType = ServiceNoteAttachmentsBindingModel.class;
			// 返回对象类型
			Type responseObjectType = null;
			ServiceNoteOperationTask serviceNoteOperationTask = new ServiceNoteOperationTask();
			serviceNoteOperationTask.setContext(this);
			serviceNoteOperationTask
					.setOnServiceNoteOperationFinishedListener(null);
			serviceNoteOperationTask.execute(new Object[] { url, requestObject,
					requestObjectType, responseObjectType });
			// 执行之后直接将取消保存的设置成未上传状态,不管返回状态
			setStateToNotUpload(model.attachments);
		} catch (Exception e) {
			CommonHelper.log(TAG + ":doNotSaveAllUploadAttachments",
					e.getMessage());
		}
	}

	// 将传入列表中的所有状态设置成未上传
	public void setStateToNotUpload(ArrayList<ServiceNoteAttachmentInfo> satList) {
		try {
			// 比较当前集合和取消保存上传结果的集合
			for (ServiceNoteAttachmentInfo serviceNoteAttachmentInfo : satList) {
				for (AttachmentInfo attachmentInfo : attachmentInfoList) {
					// 通过name进行比较
					String fileName = serviceNoteAttachmentInfo.fileName;
					if (fileName != null
							&& fileName.equals(attachmentInfo.fileName)) {
						attachmentInfo.state = AttachmentInfo.NOT_UPLOAD;
					}
				}
			}
			// 执行完后刷新视图
			attachmentAdapter.notifyDataSetChanged();
			fileInfoListView.invalidate();
		} catch (Exception e) {
			CommonHelper.log(TAG + ":setStateToNotUpload", e.getMessage());
		}

	}

	// 准备上传图片
	public void prepareUploadFile(Message msg) {
		// 标志是否所有准备上传的都上传完成
		boolean isAllUpload = true;
		for (AttachmentInfo photoInfo : attachmentInfoList) {
			if (photoInfo.state == AttachmentInfo.PREPARE_UPLOAD
					&& !TextUtils.isEmpty(photoInfo.fileName)) {
				// 有准备上传状态的,表示不是所有准备上传的都上传完成了
				isAllUpload = false;
				// 未上传并且路径不为空,则上传
				if (!isUpLoading) {
					isUpLoading = true;
					doUpLoadFile(photoInfo);
					break;
				}
			}
		}
		if (isAllUpload) {
			// 所有都上传完成,则提示是否进行保存附件操作,
			for (int i = 0; i < attachmentInfoList.size(); i++) {
				AttachmentInfo tempInfo = attachmentInfoList.get(i);
				// 只要有一个是上传完成的状态的
				if (AttachmentInfo.UP_LOADING_SUCCESS == tempInfo.state) {
					// 遍历附件列表,如果有显示上传完成的,则提示保存附件
					confirmDialog = new ConfirmDialog(AttachmentActivity.this,
							R.style.serviceNoteOperationDialog);
					// 确认保存
					confirmDialog.setAgreeListener(new View.OnClickListener() {

						@Override
						public void onClick(View v) {
							doSaveAllUploadAttachments();
						}
					});
					// 不保存
					confirmDialog
							.setDeclineListener(new View.OnClickListener() {

								@Override
								public void onClick(View v) {
									if (confirmDialog != null) {
										confirmDialog.dismiss();
										confirmDialog = null;
										doNoteSaveAllUploadAttachments();
									}
								}
							});
					confirmDialog.show();
					confirmDialog.setTvTitle("保存上传附件");
					confirmDialog.setTvContent("确认要保存上传的附件吗?");
					// 这里要break;否则弹出多个确认对话框
					break;
				}
			}
		}

	}

	// 上传图片完成的操作
	private void doUploadFileDone(Message msg) {
		try {
			// arg1带回的返回码,obj带回的String类型的消息
			int responseCode = msg.arg1;
			String resultMessage = (String) msg.obj;
			// 是否有文件正在上传标志置为否
			isUpLoading = false;
			if (responseCode == UploadUtil.UPLOAD_SUCCESS_CODE) {
				// 上传成功
				currentUpLoadPhotoInfo.state = AttachmentInfo.UP_LOADING_SUCCESS;
				// 这里保存文件在服务器的地址
				currentUpLoadPhotoInfo.path = resultMessage;
			} else if (responseCode == UploadUtil.UPLOAD_SERVER_ERROR_CODE) {
				// 上传失败
				Toast.makeText(AttachmentActivity.this, resultMessage,
						Toast.LENGTH_SHORT).show();
				currentUpLoadPhotoInfo.state = AttachmentInfo.UP_LOADING_FAIL;
			} else if (responseCode == UploadUtil.UPLOAD_FILE_NOT_EXISTS_CODE) {
				currentUpLoadPhotoInfo.state = AttachmentInfo.UP_LOADING_FAIL;
				Toast.makeText(AttachmentActivity.this, resultMessage,
						Toast.LENGTH_SHORT).show();
			}
			// 有一张图片上传完成,则发送一个继续上传的消息
			currentUploadFileTotalSize = 0;
			handler.sendEmptyMessage(TO_UPLOAD_FILE);
			attachmentAdapter.notifyDataSetChanged();
			fileInfoListView.invalidate();
		} catch (Exception e) {
			CommonHelper.log(TAG + ":doUploadFileDone", e.getMessage());
		}
	}

	// 消息处理器
	private Handler handler = new Handler() {
		@Override
		public void handleMessage(Message msg) {
			switch (msg.what) {
			// 处理上传相关消息
			case TO_UPLOAD_FILE:
				prepareUploadFile(msg);
				break;
			case UPLOAD_IN_PROCESS:
				currentUpLoadPhotoInfo.state = AttachmentInfo.UP_LOADING;
				// msg.arg1带回的是上传的进度
				currentUpLoadPhotoInfo.progress = msg.arg1;
				attachmentAdapter.notifyDataSetChanged();
				fileInfoListView.invalidate();
				break;
			case UPLOAD_FILE_DONE:
				doUploadFileDone(msg);
				break;
			// 处理下载相关消息
			case TO_DOWNLOAD_FILE:
				toDownLoadFile(msg);
				break;
			case DOWNLOAD_IN_PROCESS:
				currentDownLoadFileInfo.state = AttachmentInfo.DOWN_LOADING;
				currentDownLoadFileInfo.progress = msg.arg1;
				attachmentAdapter.notifyDataSetChanged();
				fileInfoListView.invalidate();
				break;
			case DOWNLOAD_FILE_DONE:
				doDownLoadFileDone(msg);
				break;
			default:
				break;
			}
			super.handleMessage(msg);
		}

	};

	// 下载相关消息的处理函数在下方:

	private void doDownLoadFileDone(Message msg) {
		try {
			// arg1带回的返回码,obj带回的String类型的消息
			int responseCode = msg.arg1;
			String resultMessage = (String) msg.obj;
			// 是否有文件正在上传标志置为否
			isDownLoading = false;
			if (responseCode == DownLoadUtil.DOWNLOAD_SUCCESS_CODE) {
				// 下载成功
				currentDownLoadFileInfo.state = AttachmentInfo.DOWN_LOADING_SUCCESS;
				// 将下载文件的路径改成本地路径
				currentDownLoadFileInfo.fileNativePath = subPhotoPath
						+ File.separator + currentDownLoadFileInfo.fileName;
			} else if (responseCode == DownLoadUtil.DOWNLOAD_SERVER_ERROR_CODE) {
				// 下载失败
				Toast.makeText(AttachmentActivity.this, resultMessage,
						Toast.LENGTH_SHORT).show();
				currentDownLoadFileInfo.state = AttachmentInfo.DOWN_LOADING_FAIL;
			} else if (responseCode == DownLoadUtil.DOWNLOAD_USER_CANCEL_CODE) {
				// 用户取消下载
				currentDownLoadFileInfo.state = AttachmentInfo.NOT_DOWNLOAD;
				Toast.makeText(AttachmentActivity.this, resultMessage,
						Toast.LENGTH_SHORT).show();
			}
			// 有一张图片下载完成,则发送一个继续下载下一张准备下载状态的消息
			currentDownLoadFileTotalSize = 0;
			handler.sendEmptyMessage(TO_DOWNLOAD_FILE);
			attachmentAdapter.notifyDataSetChanged();
			fileInfoListView.invalidate();
		} catch (Exception e) {
			CommonHelper.log(TAG + ":doDownLoadFileDone", e.getMessage());
		}
	}

	private void toDownLoadFile(Message msg) {
		try {
			// 标志是否所有准备下载的都下载完成
			boolean isAlldownload = true;
			// 遍历列表
			for (AttachmentInfo fileInfo : attachmentInfoList) {
				if (fileInfo.state == AttachmentInfo.PREPARE_DOWNLOAD
						&& !TextUtils.isEmpty(fileInfo.fileName)) {
					// 有准备下载状态的,表示不是所有准备下载的都下载完成了
					isAlldownload = false;
					// 未上传并且路径不为空,则上传
					if (!isDownLoading) {
						isDownLoading = true;
						doDownLoadFile(fileInfo);
						break;
					}
				}
			}
			if (isAlldownload) {

			}
		} catch (Exception e) {
			CommonHelper.log(TAG + ":doDownLoadFile", e.getMessage());
		}
	}

	// 进行下载操作
	private void doDownLoadFile(AttachmentInfo fileInfo) {
		try {
			currentDownLoadFileInfo = fileInfo;
			DownLoadUtil downLoadUtil = DownLoadUtil.getInstance();
			downLoadUtil.setCancelFlag(false);
			downLoadUtil.setOnDownLoadProcessListener(this);
			String fileUrl = Constant.UPDATE_URL + fileInfo.path;
			downLoadUtil.downLoadFile(fileUrl, subPhotoPath, fileInfo.fileName);
			currentDownLoadFileInfo.state = AttachmentInfo.DOWN_LOADING;
			attachmentAdapter.notifyDataSetChanged();
			fileInfoListView.invalidate();
		} catch (Exception e) {
			CommonHelper.log(TAG + ":doDownLoadFile", e.getMessage());
		}
	}

	@Override
	public void onUploadDone(int responseCode, String message) {
		Message msg = Message.obtain();
		msg.what = UPLOAD_FILE_DONE;
		msg.arg1 = responseCode;
		msg.obj = message;
		handler.sendMessage(msg);
	}

	@Override
	public void onUploadProcess(int uploadSize) {
		Message msg = Message.obtain();
		msg.what = UPLOAD_IN_PROCESS;
		// 获取当前上传文件的总大小,计算出上传百分比
		if (currentUploadFileTotalSize != 0) {
			Log.i(TAG, "已上传了:" + uploadSize + "总大小是:"
					+ currentUploadFileTotalSize);
			int upLoadPercent = (int) (uploadSize * 100 / currentUploadFileTotalSize);
			Log.i(TAG, "进度更新为:" + upLoadPercent);
			msg.arg1 = upLoadPercent;
			handler.sendMessage(msg);
		}
	}

	@Override
	public void initUpload(int fileSize) {
		currentUploadFileTotalSize = fileSize;
	}

	@Override
	public void onDownLoadDone(int responseCode, String message) {
		Message msg = Message.obtain();
		msg.what = DOWNLOAD_FILE_DONE;
		msg.arg1 = responseCode;
		msg.obj = message;
		handler.sendMessage(msg);
	}

	@Override
	public void onDownLoadProcess(int downLoadSize) {
		Message msg = Message.obtain();
		msg.what = DOWNLOAD_IN_PROCESS;
		// 获取当前上传文件的总大小,计算出上传百分比
		if (currentDownLoadFileTotalSize != 0) {
			Log.i(TAG, "已下载了:" + downLoadSize + "总大小是:"
					+ currentDownLoadFileTotalSize);
			int downLoadPercent = (int) (downLoadSize * 100 / currentDownLoadFileTotalSize);
			Log.i(TAG, "进度更新为:" + downLoadPercent);
			msg.arg1 = downLoadPercent;
			handler.sendMessage(msg);
		}
	}

	@Override
	public void initDownload(int fileSize) {
		currentDownLoadFileTotalSize = fileSize;
	}

}


DownLoadUtil:
package com.flagcloud.android.mqservice.biz.attachment;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;

import android.util.Log;

import com.flagcloud.android.mqservice.common.Constant;
import com.flagcloud.android.mqservice.util.CommonHelper;

public class DownLoadUtil {
	private static DownLoadUtil downLoadUtil;
	private static final String TAG = "DownLoadUtil";
	private OnDownLoadProcessListener ondownLoadProcessListener;
	private static boolean isCancel = false; // 是否停止下载的标志
	private static final String CHARSET = "utf-8"; // 设置编码
	public static final int DOWNLOAD_SUCCESS_CODE = 1;// 下载成功
	public static final int DOWNLOAD_SERVER_ERROR_CODE = 2;// 服务器出错
	public static final int DOWNLOAD_USER_CANCEL_CODE = 3;// 用户取消下载

	public static DownLoadUtil getInstance() {
		if (null == downLoadUtil) {
			downLoadUtil = new DownLoadUtil();
		}
		return downLoadUtil;
	}

	public void setCancelFlag(boolean c) {
		isCancel = c;
	}

	// 设置回调监听
	public void setOnDownLoadProcessListener(
			OnDownLoadProcessListener ondownLoadProcessListener) {
		this.ondownLoadProcessListener = ondownLoadProcessListener;
	}

	/**
	 * 开启线程执行下载操作
	 * 
	 * @param fileUrl
	 * @param destPath
	 * @param fileName
	 */
	public void downLoadFile(final String fileUrl, final String destPath,
			final String fileName) {
		// 关键是这里怎么进行处理
		new Thread(new Runnable() { // 开启线程上传文件
					@Override
					public void run() {
						toDownLoadFile(fileUrl, destPath, fileName);
					}
				}).start();
	}

	/**
	 * 
	 * @param url
	 *            请求的url
	 * @param desPath
	 *            文件存放的地址
	 * @param fileName
	 *            存放的文件名
	 */
	private void toDownLoadFile(String fileUrl, String destPath, String fileName) {
		try {
			Log.i(TAG, "doInBackground执行了...");
			String urlString = fileUrl;
			InputStream inputstream = null;
			OutputStream outputstream = null;
			HttpURLConnection urlConn = null;
			URL url;
			File resultFile = null;
			try {
				url = new URL(urlString);
				urlConn = (HttpURLConnection) url.openConnection();
				urlConn.setConnectTimeout(Constant.NETWORK_CONNECT_TIMEOUT);
				urlConn.setReadTimeout(Constant.NETWORK_TIMEOUT);
				int resCode = urlConn.getResponseCode();
				if (resCode == 200) {
					int totalLength = urlConn.getContentLength();
					ondownLoadProcessListener.initDownload(totalLength);
					inputstream = urlConn.getInputStream();
					// 重名的逻辑怎么处理
					resultFile = new File(destPath + File.separator + fileName);
					outputstream = new FileOutputStream(resultFile);
					byte[] buffer = new byte[1024];
					int len = 0;
					int progress = 0;
					while ((len = inputstream.read(buffer)) != -1) {
						if (!isCancel) {
							outputstream.write(buffer, 0, len);
							progress += len;
							// 回调更新进度
							ondownLoadProcessListener
									.onDownLoadProcess(progress);
						} else {
							return;
						}
					}
					outputstream.flush();
					sendMessage(DOWNLOAD_SUCCESS_CODE, "下载成功");
					return;
				} else {
					// 返回码不是200
					Log.i(TAG, "request error");
					sendMessage(DOWNLOAD_SERVER_ERROR_CODE, "下载失败:code="
							+ resCode);
					return;
				}
			} catch (IOException e) {
				Log.i(TAG + ":doInBackground", e.getMessage());
				sendMessage(DOWNLOAD_SERVER_ERROR_CODE,
						"下载失败:error=IOException,message=" + e.getMessage());
			} finally {
				try {
					if (outputstream != null) {
						outputstream.close();
					}
					if (inputstream != null) {
						inputstream.close();
					}
					if (urlConn != null) {
						urlConn.disconnect();
					}
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		} catch (Exception e) {
			CommonHelper.log(TAG + ":downLoadFile", e.getMessage());
			sendMessage(DOWNLOAD_SERVER_ERROR_CODE,
					"下载失败:error=" + e.getMessage());
		}
	}

	/**
	 * 发送上传结果
	 * 
	 * @param responseCode
	 * @param responseMessage
	 */
	private void sendMessage(int responseCode, String responseMessage) {
		ondownLoadProcessListener.onDownLoadDone(responseCode, responseMessage);
	}

	public static interface OnDownLoadProcessListener {
		/**
		 * 下载响应
		 * 
		 * @param responseCode
		 * @param message
		 */
		void onDownLoadDone(int responseCode, String message);

		/**
		 * 上传中
		 * 
		 * @param uploadSize
		 */
		void onDownLoadProcess(int uploadSize);

		/**
		 * 准备上传
		 * 
		 * @param fileSize
		 */
		void initDownload(int fileSize);
	}

}

UploadUtil:
package com.flagcloud.android.mqservice.biz.attachment;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Iterator;
import java.util.Map;
import java.util.UUID;
import java.util.zip.GZIPInputStream;

import com.flagcloud.android.mqservice.common.MQServiceHttpURLConnection;

import android.text.TextUtils;
import android.util.Log;

/**
 * 
 * 上传工具类
 */
public class UploadUtil {
	private static UploadUtil uploadUtil;
	private static final String BOUNDARY = UUID.randomUUID().toString(); // 边界标识,随机生成
	private static final String PREFIX = "--";
	private static final String LINE_END = "\r\n";
	private static final String CONTENT_TYPE = "multipart/form-data"; // 内容类型
	private static boolean isCancel = false;

	public void setCancelFlag(boolean c) {
		isCancel = c;
	}

	private UploadUtil() {

	}

	/**
	 * 单例模式获取上传工具类
	 * 
	 * @return
	 */
	public static UploadUtil getInstance() {
		if (null == uploadUtil) {
			uploadUtil = new UploadUtil();
		}
		return uploadUtil;
	}

	private static final String TAG = "UploadUtil";
	private int readTimeOut = 10 * 1000; // 读取超时
	private int connectTimeout = 10 * 1000; // 超时时间
	/***
	 * 请求使用多长时间
	 */
	private static int requestTime = 0;

	private static final String CHARSET = "utf-8"; // 设置编码

	/***
	 * 上传成功
	 */
	public static final int UPLOAD_SUCCESS_CODE = 1;
	/**
	 * 文件不存在
	 */
	public static final int UPLOAD_FILE_NOT_EXISTS_CODE = 2;
	/**
	 * 服务器出错
	 */
	public static final int UPLOAD_SERVER_ERROR_CODE = 3;

	public static final int UPLOAD_HEADER_ERROR_CODE = 4;
	protected static final int WHAT_TO_UPLOAD = 1;
	protected static final int WHAT_UPLOAD_DONE = 2;

	/**
	 * android上传文件到服务器
	 * 
	 * @param filePath
	 *            需要上传的文件的路径
	 * @param fileKey
	 *            在网页上<input type=file name=xxx/> xxx就是这里的fileKey
	 * @param RequestURL
	 *            请求的URL
	 */
	public void uploadFile(String filePath, String fileKey, String RequestURL) {
		if (filePath == null) {
			sendMessage(UPLOAD_FILE_NOT_EXISTS_CODE, "文件不存在");
			return;
		}
		try {
			File file = new File(filePath);
			uploadFile(file, fileKey, RequestURL);
		} catch (Exception e) {
			sendMessage(UPLOAD_FILE_NOT_EXISTS_CODE, "文件不存在");
			e.printStackTrace();
			return;
		}
	}

	/**
	 * android上传文件到服务器
	 * 
	 * @param file
	 *            需要上传的文件
	 * @param fileKey
	 *            在网页上<input type=file name=xxx/> xxx就是这里的fileKey
	 * @param RequestURL
	 *            请求的URL
	 */
	public void uploadFile(final File file, final String fileKey,
			final String RequestURL) {
		if (file == null || (!file.exists())) {
			sendMessage(UPLOAD_FILE_NOT_EXISTS_CODE, "文件不存在");
			return;
		}

		Log.i(TAG, "请求的fileName=" + file.getName());
		Log.i(TAG, "请求的fileKey=" + fileKey);
		// 关键是这里怎么进行处理
		new Thread(new Runnable() { // 开启线程上传文件
					@Override
					public void run() {
						toUploadFile(file, fileKey, RequestURL);
					}
				}).start();

	}

	private void toUploadFile(File file, String fileKey, String RequestURL) {
		String result = null;
		requestTime = 0;

		HttpURLConnection conn = null;
		DataOutputStream dos = null;
		InputStreamReader inputStreamReader = null;
		try {
			URL url = new URL(RequestURL);
			conn = (HttpURLConnection) url.openConnection();
			int beforeLength = 0;
			int endLength = 0;
			int totalLength = 0;
			StringBuffer beforeBuffer = null;
			beforeBuffer = new StringBuffer();
			beforeBuffer.append(PREFIX).append(BOUNDARY).append(LINE_END);
			beforeBuffer.append("Content-Disposition:form-data; name=\""
					+ fileKey + "\"; filename=\"" + file.getName() + "\""
					+ LINE_END);
			beforeBuffer.append(LINE_END);
			beforeLength = beforeBuffer.length();
			byte[] end_data = (LINE_END + PREFIX + BOUNDARY + PREFIX + LINE_END)
					.getBytes();
			endLength = end_data.length;
			totalLength = (int) (beforeLength + file.length() + endLength);
<span style="white-space:pre">			</span><span style="color:#ff0000;">//在开始上传之前一定要知道总的长度,才能获取上传进度</span>
			conn.setFixedLengthStreamingMode(totalLength);
			conn.setReadTimeout(readTimeOut);
			conn.setConnectTimeout(connectTimeout);
			onUploadProcessListener.initUpload(totalLength);// 初始化总长度
			// 设置基本头部
			try {
				MQServiceHttpURLConnection.SetBasicHeader(conn);
			} catch (Exception e) {
				sendMessage(UPLOAD_HEADER_ERROR_CODE, "上传结果:" + e.getMessage());
			}
			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);
			dos = new DataOutputStream(conn.getOutputStream());
			// 写内容开始
			int curLen = 0; // 当前已经传送的总长度
			dos.write(beforeBuffer.toString().getBytes());
			curLen += beforeLength;
			onUploadProcessListener.onUploadProcess(beforeLength);
			/** 上传文件 */
			InputStream inputstream = new FileInputStream(file);
			int len = 0;
			byte[] bytes = new byte[1024];
			while ((len = inputstream.read(bytes)) != -1) {
				if (!isCancel) {
					// 如果没有退出
					curLen += len;
					dos.write(bytes, 0, len);
					onUploadProcessListener.onUploadProcess(curLen);
				} else {
					return;
				}
			}
			inputstream.close();
			dos.write(end_data);
			dos.flush();
			/**
			 * 获取响应码 200=成功 当响应成功,获取响应的流
			 */
			int res = conn.getResponseCode();
			Log.e(TAG, "response code:" + res);
			if (res == 200) {
				// 成功会返回文件在服务器的地址
				String serverPath = null;
				InputStream is = null;
				is = conn.getInputStream();
				String conEncoding = conn.getContentEncoding();
				if (!TextUtils.isEmpty(conEncoding)) {
					Log.i(TAG, "contentype是:" + conEncoding);
					if (conEncoding.contains("gzip")) {
						Log.i(TAG, "创建gzip流");
						is = new GZIPInputStream(is);
					}
				} else {
					Log.i(TAG, "contentType为空");
				}
				inputStreamReader = new InputStreamReader(is);
				BufferedReader bufferReader = new BufferedReader(
						inputStreamReader);
				StringBuffer outputBuffer = new StringBuffer();
				String line = null;
				while ((line = bufferReader.readLine()) != null) {
					outputBuffer.append(line);
				}
				// 读取返回地址完成后更新进度为100%
				onUploadProcessListener.onUploadProcess(totalLength);
				// 去掉收尾的引号
				serverPath = outputBuffer.toString().substring(1,
						outputBuffer.length() - 1);
				sendMessage(UPLOAD_SUCCESS_CODE, serverPath);
				return;
			} else {
				sendMessage(UPLOAD_SERVER_ERROR_CODE, "上传失败:code=" + res);
				return;
			}
		} catch (MalformedURLException e) {
			sendMessage(
					UPLOAD_SERVER_ERROR_CODE,
					"上传失败:error=MalformedURLException,message="
							+ e.getMessage());
			e.printStackTrace();
			return;
		} catch (IOException e) {
			sendMessage(UPLOAD_SERVER_ERROR_CODE,
					"上传失败:error=IOException,message=" + e.getMessage());
			e.printStackTrace();
			return;
		} finally {
			try {
				if (dos != null) {
					dos.close();
					dos = null;
				}
				if (inputStreamReader != null) {
					inputStreamReader.close();
					inputStreamReader = null;
				}
				if (conn != null) {
					conn.disconnect();
					conn = null;
				}
			} catch (Exception e2) {
			}
		}
	}

	/**
	 * 发送上传结果
	 * 
	 * @param responseCode
	 * @param responseMessage
	 */
	private void sendMessage(int responseCode, String responseMessage) {
		onUploadProcessListener.onUploadDone(responseCode, responseMessage);
	}

	/**
	 * 下面是一个自定义的回调函数,用到回调上传文件是否完成
	 * 
	 * @author shimingzheng
	 * 
	 */
	public static interface OnUploadProcessListener {
		/**
		 * 上传响应
		 * 
		 * @param responseCode
		 * @param message
		 */
		void onUploadDone(int responseCode, String message);

		/**
		 * 上传中
		 * 
		 * @param uploadSize
		 */
		void onUploadProcess(int uploadSize);

		/**
		 * 准备上传
		 * 
		 * @param fileSize
		 */
		void initUpload(int fileSize);
	}

	private OnUploadProcessListener onUploadProcessListener;

	public void setOnUploadProcessListener(
			OnUploadProcessListener onUploadProcessListener) {
		this.onUploadProcessListener = onUploadProcessListener;
	}

	public int getReadTimeOut() {
		return readTimeOut;
	}

	public void setReadTimeOut(int readTimeOut) {
		this.readTimeOut = readTimeOut;
	}

	public int getConnectTimeout() {
		return connectTimeout;
	}

	public void setConnectTimeout(int connectTimeout) {
		this.connectTimeout = connectTimeout;
	}

	/**
	 * 获取上传使用的时间
	 * 
	 * @return
	 */
	public static int getRequestTime() {
		return requestTime;
	}

	public static interface uploadProcessListener {

	}

}


FileIconHelper:这个类就是讲后缀名和显示的缩略图做映射

package com.flagcloud.android.mqservice.biz.attachment;

import java.util.HashMap;

import com.flagcloud.android.mqservice.R;

public class FileIconHelper {

	private static final String TAG = "FileIconHelper";

	private static HashMap<String, Integer> fileExtToIcons = new HashMap<String, Integer>();

	static {
		addItem(new String[] { "mp3" }, R.drawable.mp3);
		addItem(new String[] { "wma" }, R.drawable.wma);
		addItem(new String[] { "wav" }, R.drawable.wav);
		addItem(new String[] { "mid" }, R.drawable.file_audio);
		addItem(new String[] { "mp4" }, R.drawable.mp4);
		addItem(new String[] { "wmv" }, R.drawable.wmv);
		addItem(new String[] { "mpeg" }, R.drawable.mpeg);
		addItem(new String[] { "asf" }, R.drawable.asf);
		addItem(new String[] { "avi" }, R.drawable.avi);
		addItem(new String[] { "mov" }, R.drawable.mov);
		addItem(new String[] { "vob" }, R.drawable.vob);
		addItem(new String[] { "m4v", "3gp", "3gpp", "3g2", "3gpp2" },
				R.drawable.file_video);
		addItem(new String[] { "jpg" }, R.drawable.jpg);
		addItem(new String[] { "gif" }, R.drawable.gif);
		addItem(new String[] { "png" }, R.drawable.png);
		addItem(new String[] { "bmp" }, R.drawable.bmp);
		addItem(new String[] { "ico" }, R.drawable.ico);
		addItem(new String[] { "jpeg", "wbmp" }, R.drawable.file_image);
		addItem(new String[] { "txt" }, R.drawable.txt);
		addItem(new String[] { "xml" }, R.drawable.xml);
		addItem(new String[] { "log" }, R.drawable.log);
		addItem(new String[] { "html" }, R.drawable.html);
		addItem(new String[] { "ini", "lrc" }, R.drawable.file_doc);

		addItem(new String[] { "doc", "docx" }, R.drawable.doc);
		addItem(new String[] { "ppt", "pptx" }, R.drawable.ppt);
		addItem(new String[] { "xsl", "xslx", }, R.drawable.xls);
		addItem(new String[] { "pdf" }, R.drawable.pdf);
		addItem(new String[] { "zip" }, R.drawable.zip);
		addItem(new String[] { "rar" }, R.drawable.rar);
		addItem(new String[] { "mtz", "7z" }, R.drawable.file_archive);
		addItem(new String[] { "apk" }, R.drawable.iapk);
	}

	private static void addItem(String[] exts, int resId) {
		if (exts != null) {
			for (String ext : exts) {
				fileExtToIcons.put(ext.toLowerCase(), resId);
			}
		}
	}

	public static int getFileIcon(String ext) {
		Integer i = fileExtToIcons.get(ext.toLowerCase());
		if (i != null) {
			return i.intValue();
		} else {
			return R.drawable.default_fileicon;
		}

	}
}
关键是上传下载帮助类,以及状态的控制,通过回调接口监听上传下载的状态,在回调方法中通过handler发送消息去更新图片的当前状态,更新列表和视图








【Flutter开发App开发笔记】 Flutter BottomNavgiationBar Widget 底部导航栏 切换后状态丢失 static List<Widget> contentOptions = <Widget>[ new IndexPage(), Text( 'Index 1 场馆预定', style: optionsStyle, ), PersonCenterPage(), ]; // contentOptions 是widget List 阅读详情

相关推荐

Android Multipart实现多附件上传

在Android的HttpClient实现中,不能使用Multipart实现多附件上传的功能。这里,实现了一个自定义的Multipart,用它可实现多附件上传。同时给出了Android端和服务端的例子代码。

vue2移动端实现文件下载

vue2移动端实现文件下载

qq_46617584的博客 2080

简单实现Android文件上传

文件上传在B/S应用中是一种十分常见的功能,那么在Android平台下是否可以实现像B/S那样的文件上传功能呢?答案是肯定的。下面是一个模拟网站程序上传文件的例子。 首先新建一个Android工程,新建主启动Activity: MainActivity.java: package com.xzq.upload; import java.io.DataOutputStream; import java.io.FileInputStream; import java.io.InputStream; import java.net.HttpURLConnection; import java

Android网络功能开发(4)——文件下载和上传

本文介绍了Android端用HTTP协议实现文件下载和上传的原理和代码,以及服务器端的代码。

nanoage的博客 4361

顶刊《会计研究》数据复现|最新上市公司数智化数据

本文基于Python文本分析方法构建了中国A股上市公司数智化指标(1991-2024),通过识别年报中的"机器学习""智能医疗"等关键词,量化企业数字化与智能化融合程度。研究发现数智化企业更倾向于非正式渠道的定性披露以规避风险,该指标为监管完善信披规则提供了参考。研究还揭示了数智化对企业信息披露策略的转变,并提出了未来研究方向,包括数智化对绿色创新、ESG表现等影响机制。数据覆盖非金融类上市公司,采用文本挖掘技术构建结构化指标,为相关领域研究提供了微观基础。

PPdata_Academy的博客 1487

利用GPAC生成MP4文件

GPAC主要针对学生和内容创作者,代表了一个跨平台的多媒体框架,开发人员可以使用它在 LGPL 许可下制作开源媒体。GPAC多媒体框架兼容范围广泛的流行文件类型,从常见格式(如 AVI、MPEG 和 MOV)到复杂格式(如 MPEG-4 系统或 VRML/X3D)和 360 电影。

深之JohnChen的专栏 1771

android 添加附件功能,Android实现带附件的邮件发送功能

本文实例讲解了基于基于jmail实现android邮件发送功能,分享给大家供大家参考,具体内容如下在android上发送邮件方式:第一种:借助gmail app客户端,缺点是必须使用gmail帐号,有一点是比较方便,不需要写很多代码,但是不是很灵活。第二种:基于jmail实现,可以很灵活的自己设置各种属性,不需要gmail帐号在第二种方式的实现之前,看一下jmail对email结构的划分:基于sm...

weixin_34362937的博客 600

VSCode上搭建C/C++开发环境(vscode配置c/c++环境)Windows系统---保姆级教程

关于VSCode上搭建C/C++开发环境的内容,到这里就结束了,这里博主感叹一下,VSCode真的挺强大的,作为一个编辑器,可以通过和编译器链接成为开发环境,将环境配置好的话,在这上面写代码似乎也是个不错的选择。大家有任何疑问,欢迎在评论区中提出,本篇博客有任何问题和错误也欢迎来和博主讨论,真心希望能和大家一起进步。最后,如果本篇博客对你有帮助的话,能不能给博主一个三连啊!

2303_79329831的博客 8万+

android 后台邮件发送,关于电子邮件:Android:在后台自动发送带有附件的电子邮件...

本问题已经有最佳答案,请猛点这里访问。我正在开发一个允许用户拍照并通过电子邮件自动发送到他选择的电子邮件的应用程序。 到目前为止,我能够拍照并存储在SD卡中。 现在,我只需要从文件夹(/ sdcard / Pictures / PhotoSender /)获取图片并自动发送到用户请求的电子邮件的功能。 我怎样才能做到这一点?我的照片在那个文件夹中。 我只需要一些功能即可生成电子邮件,将图片(.jp...

weixin_36378771的博客 719

炸金花游戏(3)--基于EV(期望收益)的简单AI模型

前言:   炸金花这款游戏, 从技术的角度来说, 比德州差了很多. 所以他的AI模型也相对简单一些. 本文从EV(期望收益)的角度, 来尝试构建一个简单的炸金花AI. 相关文章:  德州扑克AI--Programming Poker AI(译).  系列文章说来惭愧, 之前一直叫嚷着写德州AI, 不过可惜懒癌晚期, 一直没去实践, T_T. 相比而言,***简单很多, 也更偏重于运...

weixin_30377461的博客 2341

语义通信理论必看论文:Towards a Theory of Semantic Communication

了解语义通信发展必看的一篇文章

一去不复返的通信er 3926

android send email and pictures

http://mobile.tutsplus.com/tutorials/android/android-email-intent/   http://mobile.tutsplus.com/tutorials/android/android-sdk-sending-pictures-the-easy-way/

邓胤的家当 140

模拟调制系统实战:从AM到FM的5种调制方式对比与选型指南

本文深度解析AM、DSB、SSB、VSB、FM五种经典模拟调制方式,从实现电路、频谱效率、抗噪性能及工程成本等维度进行实战对比。通过MATLAB/Simulink仿真案例,提供清晰的选型决策框架,帮助工程师在无人机图传、短波通信、广播系统等具体项目中,根据带宽、功耗和抗干扰需求选择最优方案。

study的专栏 931

android 学习知识点累计

原文:http://blog.youkuaiyun.com/xiongyingzhuantu/article/details/6109524 1.popupwindow的使用:  View popunwindwow_layout = mLayoutInflater.inflate(                                      R.layout.popupwindow_

miracleMin的专栏 1018

论deepseek软件底层原理

如处理医学文本时,将任务分配给擅长医学知识处理的专家,避免通用模块处理的低效性,极大提升计算效率与任务处理的针对性。教师模型复杂且强大,学生模型结构简单。协作处理:多模块协作时,建立数据流通与交互机制,各模块按顺序或并行处理数据,不断传递与更新中间结果,共同完成复杂任务。(一)输入处理与任务判断 输入预处理:接收输入数据,如用户文本提问,首先进行清洗,去除噪声数据、特殊字符等。任务分类:通过MoE架构“路由器”,依据输入数据特征,判断任务所属领域与类型,如文本分类、情感分析等,为后续处理模块选择提供依据。

欢乐打代码! 2738

Allegro如何使用Cross Copy命令快速复制器件的位号和丝印外形其他层

在Allegro做PCB设计的时候,如果需要复制器件的位号到其它层是无法直接实现的,如果直接拷贝器件的位号的话,效果如下。但是Allegro的Cross Copy命令是支持复制任何属性的图形到其它层的,下面以复制器件位号和丝印外框为例说明。同样如果使用Z-copy命令,也是无法Z-copy text的,Finds下方texts是灰色的。如何使用Cross Copy命令快速复制器件的位号和丝印外形其他层。另外cap处可以选择线段的端点是圆形,八角形,或者方形的铜皮。拷贝C1013,出现的是C*

weixin_54787054的博客 2671

android字体样式带图,PoorEdit: Android平台富文本编辑器控件 支持加粗,斜体,下划线,高亮等字体样式 支持todo及列表 支持图片插入 支持附件插入 支持JSON导入导出...

PoorEditRich Text Editor for AndroidStatusWIP. So those who forked this repo may need to merge my code manually.Buggy but almost there!ScreenshotsI'm tring to make it look & feel like you are stil...

weixin_39774808的博客 260

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

Cordova Local-Notification Plugin跨平台适配:Android与iOS差异详解

Cordova Local-Notification Plugin是一款强大的跨平台通知插件,能够帮助开发者在Android和iOS应用中实现丰富的本地通知功能。然而,由于Android和iOS系统架构的差异,开发者在实现跨平台通知时需要特别注意两者的不同之处,以确保应用在不同设备上都能提供一致且优质的用户体验。 ## 📱 权限请求机制差异 Android和iOS在通知权限请求方面存在明显差

gitblog_00393的博客 944

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

HiPDA·NG:一款HiPDA论坛的非官方客户端

HiPDA·NG:一款HiPDA论坛的非官方客户端 HiPDA·NG 是一款基于 Android 平台的开源项目,主要使用 Java 编程语言开发。该项目旨在为 HiPDA 论坛提供一个非官方的客户端,使得用户可以更加便捷地访问论坛内容。 核心功能 论坛内容浏览:用户可以浏览 HiPDA 论坛中的帖子,进行阅读和回复。 个性化设置:支持夜间模式、自定义字体大小等个性化设置,提升用户体验。 图片浏...

gitblog_00785的博客 886

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

intent调用代码总结

http://gundumw100.iteye.com/blog/851056 进入联系人界面 Intent intent = new Intent(); intent.setAction(Intent.ACTION_VIEW); intent.setData(People.CONTENT_URI); startActivity(intent); 查看某个联系人,当

qianguming的专栏 697

通达OA公用组件使用指南

文件附件处理是OA系统的核心功能之一,涉及文件上传、存储、下载、预览、删除等多个环节。本文将深入解析OA的文件附件处理机制,包括上传流程、存储结构、权限控制和最佳实践。:doc, docx, xls, xlsx, ppt, pptx, pdf等。:jpg, jpeg, png, gif, bmp等。:替换HTML内容中的图片路径为附件URL。:mp3, mp4, avi, flv等。:判断文件是否为Office文件。:生成完整的附件链接HTML。:用于生成安全的附件访问链接。:判断文件是否支持在线预览。

187

android 添加附件功能,Android开发之实现附件的邮件发送功能

在Android开发中,都会开发附件的邮件这个功能,而点击按钮也是也是方便用户输入的需求,爱站技术频道小编今天就带大家进入下文来了解Android开发之实现附件的邮件发送功能,感兴趣的小伙伴们可以参考看看。在android上发送邮件方式:第一种:借助GMail APP客户端,缺点是必须使用GMail帐号,有一点是比较方便,不需要写很多代码,但是不是很灵活。第二种:基于JMail实现,可以很灵活的自...

weixin_33735281的博客 749

保存彩信图片到sd卡指定文件夹中

1、在SmsDetailLeftRow.java文件中找到setOnClickListener()方法,向其中添加长按事件: private void setOnClickListener(final MessageItem msgItem) {         switch(msgItem.mAttachmentType) {         case WorkingMessage.IMA

sunxingzhesunjinbiao的专栏 1622

安卓手机云便签图片录音及附件文件的存储位置在哪里?

在敬业签云便签中记录的文字、图片、音频以及附件文件等内容默认存储位置是云端服务器空间中,安卓手机端可以将便签图片、录音音频或者附件等内容资料下载保存到本地,以华为手机为例,存储位置如下: 一、打开手机版云便签敬业签并登录账号,找到一条添加有声音、图片或者附件文件的便签内容,点击进行内容详情页面,找到图片、录音或者附件文件下载保存到本地; 二、在手机桌面或者本地文件夹中找到“文件管理”应用,点击打开在“浏览”中找到“我的手机”,打开我的手机文件页面; 三、在文件页面找到Android>data&gt

yunbinjier的博客 1740

android webview 附件上传图片,Android Webview实现图片、文件上传及启动相机功能

直接上代码,体会Webview强大的功能。webView.setWebChromeClient(webChromeClient);private String mCameraFilePath = null;private ValueCallback mUploadMessage;// 表单的数据信息private final static int FILECHOOSER_RESULTCODE = ...

weixin_31445091的博客 306
上一篇: Unable to execute dex: java.nio.BufferOverflowException
下一篇: android.database.StaleDataException: Attempted to access a cursor after it has been clos
HDPCOMEON
博客等级 码龄15年 1粉丝 21原创
评论 3
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值