前言:
本文介绍的是android HttpUrlConnection实现的文件上传,接触过web的,大家都知道表单文件上传我们只需要指定enctype="multipart/form-data" method="post"就可以了。这是因为浏览器浏览器已经帮我构建了消息头,而在android中我们需要自己拼接复杂的消息头,来实现文件上传。本人的第一篇博文,希望大家多多支持!!!
Android端实现:
下面是android端文件上传的主要方法,我们可以 传递普通数据和文件,参数通过params和files已经加以区分了,同时可以一次上传多个文件。
public static String uploadFile(String url, Map<String, String> params,
Map<String, File> files) throws IOException {
String result = "";
String BOUNDARY = java.util.UUID.randomUUID().toString();
String PREFIX = "--", LINEND = "\r\n";
String MULTIPART_FROM_DATA = "multipart/form-data";
String CHARSET = "UTF-8";
URL uri = new URL(url);
HttpURLConnection conn = (HttpURLConnection) uri.openConnection();
conn.setReadTimeout(5 * 1000); // 缓存的最长时间
conn.setDoInput(true);// 允许输入
conn.setDoOutput(true);// 允许输出
conn.setUseCaches(false); // 不允许使用缓存
conn.setRequestMethod("POST");
conn.setRequestProperty("connection", "keep-alive");
conn.setRequestProperty("Charsert", "UTF-8");
conn.setRequestProperty("Content-Type", MULTIPART_FROM_DATA
+ ";boundary=" + BOUNDARY);
// 首先发送普通数据
StringBuilder sb = new StringBuilder();
for (Map.Entry<String, String> entry : params.entrySet()) {
sb.append(PREFIX);
sb.append(BOUNDARY);
sb.append(LINEND);
sb.append("Content-Disposition: form-data; name=\""
+ entry.getKey() + "\"" + LINEND);
sb.append("Content-Type: text/plain; charset=" + CHARSET + LINEND);
sb.append("Content-Transfer-Encoding: 8bit" + LINEND);
sb.append(LINEND);
sb.append(entry.getValue());
sb.append(LINEND);
}
DataOutputStream outStream = new DataOutputStream(
conn.getOutputStream());
outStream.write(sb.toString().getBytes("UTF-8"));
// 发送文件数据
if (files != null)
for (Map.Entry<String, File> file : files.entrySet()) {
StringBuilder sb1 = new StringBuilder();
sb1.append(PREFIX);
sb1.append(BOUNDARY);
sb1.append(LINEND);
sb1.append("Content-Disposition: form-data; name=\"file\"; filename=\""
+ file.getKey() + "\"" + LINEND);
sb1.append("Content-Type: application/octet-stream; charset="
+ CHARSET + LINEND);
sb1.append(LINEND);
outStream.write(sb1.toString().getBytes("UTF-8"));
InputStream is = new FileInputStream(file.getValue());
byte[] buffer = new byte[1024];
int len = 0;
while ((len = is.read(buffer)) != -1) {
outStream.write(buffer, 0, len);
}
is.close();
outStream.write(LINEND.getBytes());
}
// 请求结束标志
byte[] end_data = (PREFIX + BOUNDARY + PREFIX + LINEND).getBytes();
outStream.write(end_data);
outStream.flush();
// 得到响应码
int res = conn.getResponseCode();
InputStream in = conn.getInputStream();
if (res == 200) {
int ch;
StringBuilder sb2 = new StringBuilder();
while ((ch = in.read()) != -1) {
sb2.append((char) ch);
}
result = sb2.toString();
}
outStream.close();
conn.disconnect();
return result;
}
首先,看代码。我们构造了消息头。对于消息头的格式大家可以使用抓包工具,来抓取。然后我们设置一些连接属性,分隔符等,最后开始传递普通数据以及文件,代码的注释也比较详细,这里不过多说明了。调用的代码接下来。由于是耗时操作,我使用了AsyncTask来实现异步,具体的逻辑,界面更新大家可以更加需求,自己实现代码。
public void uploadFile(String url, Map<String, String> normalMap,
Map<String, File> files) {
new AsyncTask<Object, Void, String>() {
@Override
protected String doInBackground(Object... params) {
// TODO Auto-generated method stub
String url = (String) params[0];
String result = null;
Map<String, String> normalMap = (Map<String, String>) params[1];
Map<String, File> files = (Map<String, File>) params[2];
try {
result = HttpUtil.uploadFile(url, normalMap, files);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return result;
}
@Override
protected void onPostExecute(String result) {
// TODO Auto-generated method stub
super.onPostExecute(result);
//实现界面更新
}
}.execute(new Object[] { url, normalMap, files });
}
服务端:
服务端本人是通过java web servlet来实现的,主要借助了apache的 commons-fileupload-1.2.1.jar,关于文件上传的jar包大家可以网上下载,下面见主要代码
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
DataOutputStream outputStream = new DataOutputStream(response.getOutputStream());
// 上传文件的目录,放在WEB-INF下面,不允许外界直接访问
String uploadPath = getServletContext().getRealPath("/WEB-INF/upload");
// 临时文件目录
String tempPath = getServletContext().getRealPath("/WEB-INF/temp");
File tmpFile = new File(tempPath);
if (!tmpFile.exists()) {
// 创建临时目录
tmpFile.mkdir();
}
try {
// Create a factory for disk-based file items
DiskFileItemFactory factory = new DiskFileItemFactory();
// 设置工厂的缓冲区的大小,当上传的文件大小超过缓冲区的大小时,就会生成一个临时文件存放到指定的临时目录当中。默认10KB
factory.setSizeThreshold(1024 * 100);
factory.setRepository(tmpFile);// 设置缓冲区目录
ServletFileUpload upload = new ServletFileUpload(factory);
// 解决上传文件名的中文乱码
upload.setHeaderEncoding("UTF-8");
// 设置单个最大文件尺寸,这里是4MB
upload.setFileSizeMax(4 * 1024 * 1024);
// 设置上传文件总量的最大值,最大值=同时上传的多个文件的大小的最大值的和,目前设置为10MB
upload.setSizeMax(1024 * 1024 * 10);
// 监听文件上传进度
upload.setProgressListener(new ProgressListener() {
public void update(long pBytesRead, long pContentLength,
int arg2) {
System.out.println("文件大小为:" + pContentLength + ",已上传:"
+ pBytesRead);
}
});
// 3、判断提交上来的数据是否是上传表单的数据
if (!ServletFileUpload.isMultipartContent(request)) {
// 按照传统方式获取数据
return;
}
List<FileItem> items = upload.parseRequest(request);// 解析封装请求
for (FileItem item : items) {
if (item.isFormField()) {// 普通数据
String name = item.getFieldName();
// 解决普通输入项的数据的中文乱码问题
String value = item.getString("UTF-8");
System.out.println(name + "=" + value);
} else {// 文件类型
String filename = item.getName();
if (filename == null || filename.trim().equals("")) {
continue;
}
// 不同的浏览器提交的文件名是不一样的,只保留文件名部分
filename = filename
.substring(filename.lastIndexOf("\\") + 1);
// 得到上传文件的扩展名
String fileExtName = filename.substring(filename
.lastIndexOf(".") + 1);
// 如果需要限制上传的文件类型,那么可以通过文件的扩展名来判断上传的文件类型是否合法
System.out.println("上传的文件的扩展名是:" + fileExtName);
// 获取item中的上传文件的输入流
InputStream in = item.getInputStream();
// 得到文件保存的名称
String saveFilename = makeFileName(filename);
// 得到文件的保存目录
String realSavePath = makePath(saveFilename, uploadPath);
// 创建一个文件输出流
FileOutputStream out = new FileOutputStream(realSavePath
+ "\\" + saveFilename);
// 创建一个缓冲区
byte buffer[] = new byte[1024];
// 判断输入流中的数据是否已经读完的标识
int len = 0;
// 循环将输入流读入到缓冲区当中,(len=in.read(buffer))>0就表示in里面还有数据
while ((len = in.read(buffer)) > 0) {
out.write(buffer, 0, len);
}
// 关闭输入流
in.close();
// 关闭输出流
out.close();
// 删除处理文件上传时生成的临时文件
item.delete();
outputStream.writeBytes("YES");
}
}
System.out.print("upload succeed");
} catch (FileUploadBase.FileSizeLimitExceededException e) {
e.printStackTrace();
System.out.println("-------------单个文件超出最大值----------------");
return;
} catch (FileUploadBase.SizeLimitExceededException e) {
e.printStackTrace();
System.out
.println("--------------上传文件的总的大小超出限制的最大值----------------");
return;
} catch (Exception e) {
e.printStackTrace();
}
}
代码里面的注释已经很详细了,大家可以看看。最后别忘了在web.xml里面配置我们的servlet,ok,至此一个简单的文件上传功能就基本完成了。
总结:
HttpClient类好像已经被google弃用了,使用原生的HttpUrlConnection感觉实现起来还是过于繁琐,大家可以试着使用一些开源框架okhttp、xutils、volley等,实现起来很简单,稳定性也比较高,以后有机会再一起学习,see you!