最初,我是想实现一个可看见上传进度的效果。
在网络上查找了资料,资料虽少,不过稍加研究,还是实现了效果。
在此,先向前辈们表达敬意。
我将相关代码和思路进行一些整理,一并发出,方便大家参考。
1,需要依赖的包
apache-mime4j-0.6.jar;httpmime-4.0.2.jar
这两个包我一并上传。
2,核心类
ProcessEntity
继承自MultipartEntity。
为了获取上传进度,需要加入一个监听器以及自定义输出流。
package org.ashtray.single;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.Charset;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntity;
import org.ashtray.single.listener.ProgressListner;
import org.ashtray.single.stream.CountingOutputStream;
/**
* 自定义文件上传实体类
*
* @author Ashtray
* @createtime 2012-9-29 上午11:36:05 最后修改时间 : 更新记录:
*/
public class ProcessEntity extends MultipartEntity {
/**上传进度监听器*/
private ProgressListner listener;
public ProcessEntity(ProgressListner listener) {
super();
this.listener = listener;
}
public ProcessEntity(HttpMultipartMode mode, ProgressListner listener) {
super(mode);
this.listener = listener;
}
public ProcessEntity(HttpMultipartMode mode,String boundary,Charset charset,ProgressListner listener) {
super(mode, boundary, charset);
this.listener = listener;
}
@Override
public void writeTo(OutputStream out) throws IOException {
super.writeTo(new CountingOutputStream(out,listener));
}
}
ProgressListner
上传进度监听器,上传文件时回调,获取当前上传字节数,更新UI进度条。
package org.ashtray.single.listener;
/**
* 监听器,上传文件时回调,获取当前上传字节数。
*
* @author Ashtray
* @createtime 2012-9-29 下午01:45:47
* 最后修改时间 :
* 更新记录:
*/
public interface ProgressListner {
/**
* 文件上传过程中被回调,
* 获取当前上传字节数。
*
* @param count 当前上传字节数
* @author Ashtray
* @createtime 2012-10-11 上午11:16:09
* 最后修改时间 :
* 更新记录:
*/
void transferred(long count);
}
CountingOutputStream
自定义输出流,继承自FilterOutputStream。
用于记录已上传的字节数,并告知监听器。
package org.ashtray.single.stream;
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import org.ashtray.single.listener.ProgressListner;
/**
* 自定义输出流,
* 在文件上传时记录已上传的字节数,
* 并回调监听器。
*
* @author Ashtray
* @createtime 2012-9-29 下午01:58:07
* 最后修改时间 :
* 更新记录:
*/
public class CountingOutputStream extends FilterOutputStream {
/**上传进度监听器*/
private ProgressListner listener;
/**已上传字节数*/
private long transferred;
public CountingOutputStream(OutputStream out,ProgressListner listener) {
super(out);
this.listener = listener;
}
@Override
public void write(byte [] buffer,int offset,int count) throws IOException {
out.write(buffer, offset, count);
//记录已上传的字节数
transferred += count;
//回调监听器方法
listener.transferred(transferred);
}
@Override
public void write(int oneByte) throws IOException {
out.write(oneByte);
transferred++;
listener.transferred(transferred);
}
}
HttpMultipartPost
文件上传异步任务,用于读取本地文件,创建并发送HTTP请求。
上传过程中更新UI。
package org.ashtray.single.async;
import java.io.File;
import java.io.IOException;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.impl.client.DefaultHttpClient;
import org.ashtray.single.Const;
import org.ashtray.single.ProcessEntity;
import org.ashtray.single.activity.ImageUpload;
import org.ashtray.single.http.HttpCenter;
import org.ashtray.single.listener.ProgressListner;
import org.ashtray.single.util.Debug;
import android.app.ProgressDialog;
import android.os.AsyncTask;
/**
* 上传文件异步任务,
* 便于实时更新UI中的进度条。
*
* @author Ashtray
* @createtime 2012-9-29 下午02:15:51
* 最后修改时间 :
* 更新记录:
*/
public class HttpMultipartPost extends AsyncTask<Void, Integer, HttpResponse> {
/**上下文*/
private ImageUpload activity;
/**为简便,使用系统自带的进度条对话框*/
private ProgressDialog dialog;
/**上传文件大小(实际上并非完全是待上传文件的大小,而是在文件基础上加入了部分描述信息)*/
private long size;
public HttpMultipartPost(ImageUpload activity){
this.activity = activity;
}
@Override
protected void onPreExecute() {
//初始化进度对话框,并显示
dialog = new ProgressDialog(activity);
dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
dialog.setMessage("Uploading Image");
dialog.setCancelable(true);
dialog.show();
}
@Override
protected HttpResponse doInBackground(Void... params) {
//创建HTTP请求
HttpClient client = new DefaultHttpClient(HttpCenter.getParams());
HttpPost post = new HttpPost(Const.URL_BYTE);
//构建上传文件实体,注意匿名的监听器
ProcessEntity entity = new ProcessEntity(new ProgressListner() {
@Override
public void transferred(long count) {
//此处必须这样,否则不能得出正确的计算结果
int progress = (int) ((count / (float) size) * 100);
//更新UI
publishProgress(progress);
}
});
try {
//读取本地文件,并加入请求实体
File file = new File(Const.PATH);
entity.addPart("file", new FileBody(file));
size = entity.getContentLength();
//注意此处的差值
Debug.log("文件:" + file.length() + "byte,请求:" + size + ",多余:" + (size - file.length()));
//发送POST请求进行上传
post.setEntity(entity);
HttpResponse resp = client.execute(post);
return resp;
} catch (ClientProtocolException e) {
e.printStackTrace();
return null;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
@Override
protected void onProgressUpdate(Integer... values) {
//更新进度
dialog.setProgress(values[0]);
}
@Override
protected void onPostExecute(HttpResponse result) {
dialog.dismiss();
}
}
Activity略,只需调用new HttpMultipartPost(this).execute();即可。
3,服务端
服务端接收文件时会有一个问题,网络上的资料都没有提及。我找到了原因和一个不算很好的解决方法。
详见后述。
服务端即基本的Servlet即可。
package com.th.server.web.servlet;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.UUID;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 接收上传的文件,并保存在本地
*
* @author Ashtray
* @createtime 2012-9-25 下午05:02:53
* 最后修改时间 :
* 更新记录:
*/
public class Upload extends HttpServlet {
private static final long serialVersionUID = 2478185387706416494L;
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setCharacterEncoding("UTF-8");
System.out.println(req.getContentType());
try {
InputStream is = req.getInputStream();
OutputStream os = new FileOutputStream("D:/" + UUID.randomUUID() + ".jpg");
byte [] buffer = new byte [1024];
int length = -1;
//以MultipartEntity的方式接收上传文件,流中会包含描述信息,
//去掉第一次读取的内容,即可除去,保证只接收文件内容
boolean first = true;
while ((length = is.read(buffer)) != -1) {
if (!first) {
os.write(buffer, 0, length);
}
first = false;
}
os.flush();
is.close();
os.close();
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
doPost(req, resp);
}
}
如前文所述,采用MultipartEntity上传文件会产生一个问题。
会在请求中加入描述信息,导致Servlet中输出的文件会多余一部分内容,即前文中提到的一个差值。
用记事本将接收到的文件打开,会看到如下内容。
--aW3AQg4cMF7JA21OHAMMR9JaNlrj7nTAP
Content-Disposition: form-data; name="file"; filename="fe5bc4e1-8ecb-464c-9266-1794345a4a75"
Content-Type: application/octet-stream
Content-Transfer-Encoding: binary
??5Exif...以下均为二进制内容
--aW3AQg4cMF7JA21OHAMMR9JaNlrj7nTAP--
--aW3AQg4cMF7JA21OHAMMR9JaNlrj7nTAP称为boundary,在服务端可通过req.getContentType()得到。
问题的关键就在于,如何去掉上传文件头部的那部分多余的内容。
本来希望由客户端提供多余内容的长度,服务端舍弃这一部分内容。
但是在实际测试中发现,并不需要如此精确,将第一次读取到的内容舍去即可。
如以上代码所示。
这样就能保证接收到文件正确,实际上还是有点小问题,文件尾部的boundary没有去掉,但是不会影响整个文件的正确性。
这个解决办法比较山寨,如有更好的办法,望不吝赐教。
完
分享到:
相关推荐
主要介绍了Google新近推出的移动设备软件平台Android以及基于此平台的移动终端应用程序开发
《单片机应用技术(C语言)》实训课件—6.2 移动终端数据上传系统设计.pdf《单片机应用技术(C语言)》实训课件—6.2 移动终端数据上传系统设计.pdf《单片机应用技术(C语言)》实训课件—6.2 移动终端数据上传系统设计....
为了确保移动终端的信息安全,针对一种结合移动云计算技术的移动终端安全认证方法进行研究,设计基于多跳接入的最优截断带宽分配方案D2MSMC网络覆盖扩展机制,对无线网络拓扑结构进行优化。D2MSMC算法最大化了认证...
对移动安全的博士论文,非常详细的,如若有兴趣研究移动安全的,这是篇不错的文章
移动终端多媒体数据同步系统研究与实现,吴益坚,卢美莲,移动互联网技术的发展带来了信息产业的又一轮革命,人们将使用更多类型的终端获取信息。与此同时,移动数据的数量和内容将呈几何
基于Android的移动终端应用程序开发与研究.pdf
Android智能移动终端应用开发实验报告.docxAndroid智能移动终端应用开发实验报告.docxAndroid智能移动终端应用开发实验报告.docxAndroid智能移动终端应用开发实验报告.docxAndroid智能移动终端应用开发实验报告....
面向移动终端的移动P2P文件共享服务研究,葛远庭,屈志毅,在现有移动计算和P2P技术的基础上,提出了一种面向移动终端(如手机、PDA等)的混合型移动P2P文件共享服务模型,并对其网络架构和业
人脸识别、移动终端身份认证系统、聚类分析
基于云计算的智能移动终端的数据安全性研究.pdf
随着移动计算的普及和3G技术的发展,人们对移动通信终端功能的要求不断提高,移动通信终端操作系统在受到广泛关注和研究的同时,也面临着新的问题和挑战。全面而系统地介绍了移动通信终端操作系统的基本概念、主要...
《移动终端应用开发技术--Android实战(全国高等职业教育规划教材)》(作者林少丹)共分三部分,第一部分“准备篇”,对Android系统的各个层面进行了详细讲解,旨在让读者在尽量短的时间内对Andriod系统的各个方面有一...
智能移动终端安全研究,刘金鑫,吴昊,伴随着移动互联网的发展,智能终端的使用数量急剧增加,功能也日益增强。但与此同时,由于智能终端本身的开放性、灵活性,以及智
为了提高移动交互的可用性,进行了用非语音声音来增强移动终端菜单交互的研究,首先设计了一个移动终端的声音增强菜单,然后通过与相应的视觉菜单的对比实验来研究其是否能提高可用性。结果显示,声音增强移动菜单...
介绍移动终端定位技术的主要组织、技术体系、发展现状与趋势,讨论了移动终端定位的应用模式及其业务发展的主要制约因素,阐述了发展移动终端定位技术的机遇与挑战,并提出我国发展移动终端定位技术的主要对策。
基于MAS的移动终端适配技术的应用,陈 栋,李学明,本文通过一个web页面的移动终端适配系统项目,描述了一种适用于企业集团用户的,由各个功能插件及管理模块组成的移动终端适配技术
基于Android平台的移动终端应用程序的研究与开发
面向移动终端的文件备份索引网络设计,孙一丹,郭燕慧,随着移动终端应用的日益广泛,移动终端存储的数据也越来越多样,数据备份作为保障数据安全的重要方法之一备受关注。本文在分析移