【文章标题】Android下的应用编程——用HTTP协议实现文件上传功能

【文章作者】曾健生

【作者邮箱】zengjiansheng1@126.com

【作者QQ190678908

【作者MSNzengjiansheng1@hotmail.com

【作者博客】blog.csdn.net/newjueqi

*******************************************************************************

Android的客户端编程中(特别是SNS 类型的客户端),经常需要实现注册功能Activity,要用户输入用户名,密码,邮箱,照片后注册。但这时就有一个问题,在HTML中用form表单就能实现如上的注册表单,需要的信息会自动封装为完整的HTTP协议,但在Android中如何把这些参数和需要上传的文件封装为HTTP协议呢?

我们可以先做个试验,看一下form表单到底封装了什么样的信息。

第一步:编写一个Servlet,把接收到的HTTP信息保存在一个文件中,代码如下:

public void doPost(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {

//获取输入流,是HTTP协议中的实体内容

ServletInputStream sis=request.getInputStream();

//缓冲区

byte buffer[]=new byte[1024];

FileOutputStream fos=new FileOutputStream("d://file.log");

int len=sis.read(buffer, 0, 1024);

//把流里的信息循环读入到file.log文件中

while( len!=-1 )

{

fos.write(buffer, 0, len);

len=sis.readLine(buffer, 0, 1024);

}

fos.close();

sis.close();

}

第二步:实现如下图1的的表单页面,生成一个注册表单,提交到Servlet

1

详细的代码如下:

<form action="servlet/ReceiveFile" method="post" enctype="multipart/form-data">

第一个参数<input type="text" name="name1"/> <br/>

第二个参数<input type="text" name="name2"/> <br/>

第一个上传的文件<input type="file" name="file1"/> <br/>

第二个上传的文件<input type="file" name="file2"/> <br/>

<input type="submit" value="提交">

</form>

注意了,由于要上传附件,所以一定要设置enctypemultipart/form-data,才可以实现附件的上传。

第三步:填写完信息后按“提交”按钮后,在D盘下查找file.log文件用记事本打开,数据如下:

-----------------------------7d92221b604bc

Content-Disposition: form-data; name="name1"

hello

-----------------------------7d92221b604bc

Content-Disposition: form-data; name="name2"

world

-----------------------------7d92221b604bc

Content-Disposition: form-data; name="file1"; filename="C:/2.GIF"

Content-Type: image/gif

GIF89a

€ €€ €€ € €€€€€?     3 f  3 33 3f 3 3 3 f f3 ff f f f ? ? ? ?  3 f   3 3 33 f3 ? ? 33 33333f33?3?33f 3f33ff3f?f?f3 3?3333?3 3?3333?3 333f3??f f 3f ff f3 f33f3ff333ff ff3ffffffff f?ffff?f f?ffff?f f3fff 3 f ? ?3?f?33 3f檉櫃ff 3f櫃櫃櫶櫃 3f櫶櫃烫櫶? ?3?f? 3 f ? ?3?f?33 3f蘤櫶ff 3f虣櫶櫶虣 3f烫櫶烫烫? ?3?f?  3 f ? ? 3 333f3?3?3f f3fff?f?f ???? 3f??!? ,

e  ??QM!ClH馉脝远5p?3R?R?MV39V5?谈re? 3??qn?Qc?iEW艗赥戟j ;

-----------------------------7d92221b604bc

Content-Disposition: form-data; name="file2"; filename="C:/2.txt"

Content-Type: text/plain

hello everyone!!!

-----------------------------7d92221b604bc--

从表单源码可知,表单上传的数据有4个:参数name1name2,文件file1file2

首先从file.log观察两个参数name1name2的情况。这时候使用UltraEdit打开file.log(因为有些字符在记事本里显示不出来,所以要用16进制编辑器),如图2

2

结合16进制数据和记事本显示的数据可知上传参数部分的格式规律:

1. 第一行是“-----------------------------7d92221b604bc”作为分隔符,然后是“/r/n”(即16进制编辑器显示的0D 0A)回车换行符。

2. 第二行

(1) 首先是HTTP中的扩展头部分“Content-Disposition: form-data;”,表示上传的是表单数据。

(2) name="name1"”参数的名称。

(3) /r/n”(即16进制编辑器显示的0D 0A)回车换行符。

3. 第三行:“/r/n”(即16进制编辑器显示的0D 0A)回车换行符。

4. 第四行:参数的值,最后是“/r/n”(即16进制编辑器显示的0D 0A)回车换行符。

由观察可得,表单上传的每个参数都是按照以上14的格式构造HTTP协议中的参数部分。

那么我们再来看上传的文件部分(以第二个上传的文件2.txt为例)图3

3

结合16进制数据和记事本显示的数据可知上传文件部分的格式规律:

1. 第一行是“-----------------------------7d92221b604bc”作为分隔符,然后是“/r/n”(即16进制编辑器显示的0D 0A)回车换行符。

2. 第二行:

a) 首先是HTTP中的扩展头部分“Content-Disposition: form-data;”,表示上传的是表单数据。

b) name="file2";”参数的名称。

c) filename="C:/2.txt"”参数的值。

d) /r/n”(即16进制编辑器显示的0D 0A)回车换行符。

3. 第三行:HTTP中的实体头部分“Content-Type: text/plain”:表示所接收到得实体内容的文件格式。计算机的应用中有多种多种通用的文件格式,人们为每种通用格式都定义了一个名称,称为MIMEMIME的英文全称是"Multipurpose Internet Mail Extensions" (多功能Internet 邮件扩充服务)

4. 第四行:“/r/n”(即16进制编辑器显示的0D 0A)回车换行符。

5. 第五行开始:上传的内容的二进制数。

6. 最后是结束标志“-----------------------------7d92221b604bc--”,注意:这个结束标志和分隔符的区别是最后多了“--”部分。

但现在还有一个问题,就是分隔符“-----------------------------7d92221b604bc”是怎么确定的呢?是不是一定要“7d92221b604bc”这串数字?

我们以前的分析只是观察了HTTP请求的实体部分,可以借用工具观察完整的HTTP请求看一看有没有什么线索?

IE下用HttpWatch,在Firefox下用Httpfox这个插件,可以实现网页数据的抓包,结果如下图:

图4

从图4可看出,原来在Content-Type部分指定了分隔符所用的字符串。

根据以上总结的注册表单中的参数传递和文件上传的规律,我们可以能写出Android中实现一个用户注册功能(包括个人信息填写和上传图片部分)的工具类,

首先,要有一个javaBeanFormFile封装文件的信息:

public class FormFile { /* 上传文件的数据 */ private byte[] data; /* 文件名称 */ private String filname; /* 表单字段名称*/ private String formname; /* 内容类型 */ private String contentType = "application/octet-stream"; //需要查阅相关的资料 public FormFile(String filname, byte[] data, String formname, String contentType) { this.data = data; this.filname = filname; this.formname = formname; if(contentType!=null) this.contentType = contentType; } public byte[] getData() { return data; } public void setData(byte[] data) { this.data = data; } public String getFilname() { return filname; } public void setFilname(String filname) { this.filname = filname; } public String getFormname() { return formname; } public void setFormname(String formname) { this.formname = formname; } public String getContentType() { return contentType; } public void setContentType(String contentType) { this.contentType = contentType; } }

实现文件上传的代码如下:

/** * 直接通过HTTP协议提交数据到服务器,实现表单提交功能 * @param actionUrl 上传路径 * @param params 请求参数 key为参数名,value为参数值 * @param file 上传文件 */ public static String post(String actionUrl, Map<String, String> params, FormFile[] files) { try { String BOUNDARY = "---------7d4a6d158c9"; //数据分隔线 String MULTIPART_FORM_DATA = "multipart/form-data"; URL url = new URL(actionUrl); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setDoInput(true);//允许输入 conn.setDoOutput(true);//允许输出 conn.setUseCaches(false);//不使用Cache conn.setRequestMethod("POST"); conn.setRequestProperty("Connection", "Keep-Alive"); conn.setRequestProperty("Charset", "UTF-8"); conn.setRequestProperty("Content-Type", MULTIPART_FORM_DATA + "; boundary=" + BOUNDARY); StringBuilder sb = new StringBuilder(); //上传的表单参数部分,格式请参考文章 for (Map.Entry<String, String> entry : params.entrySet()) {//构建表单字段内容 sb.append("--"); sb.append(BOUNDARY); sb.append("/r/n"); sb.append("Content-Disposition: form-data; name=/""+ entry.getKey() + "/"/r/n/r/n"); sb.append(entry.getValue()); sb.append("/r/n"); } DataOutputStream outStream = new DataOutputStream(conn.getOutputStream()); outStream.write(sb.toString().getBytes());//发送表单字段数据 //上传的文件部分,格式请参考文章 for(FormFile file : files){ StringBuilder split = new StringBuilder(); split.append("--"); split.append(BOUNDARY); split.append("/r/n"); split.append("Content-Disposition: form-data;name=/""+ file.getFormname()+"/";filename=/""+ file.getFilname() + "/"/r/n"); split.append("Content-Type: "+ file.getContentType()+"/r/n/r/n"); outStream.write(split.toString().getBytes()); outStream.write(file.getData(), 0, file.getData().length); outStream.write("/r/n".getBytes()); } byte[] end_data = ("--" + BOUNDARY + "--/r/n").getBytes();//数据结束标志 outStream.write(end_data); outStream.flush(); int cah = conn.getResponseCode(); if (cah != 200) throw new RuntimeException("请求url失败"); InputStream is = conn.getInputStream(); int ch; StringBuilder b = new StringBuilder(); while( (ch = is.read()) != -1 ){ b.append((char)ch); } outStream.close(); conn.disconnect(); return b.toString(); } catch (Exception e) { throw new RuntimeException(e); } }

更多相关文章

  1. Android中的DrawRect()参数解析
  2. Android参数设置: Preference
  3. Android cmd 命令常用参数详解
  4. android 10.0拍照显示并上传服务器
  5. Android实现文件上传功能(接收端用strust2)
  6. Android WebView加载HTML表单并通过javascript提交
  7. WebView中调用系统相册或拍照上传
  8. 工程 android上传图片至服务器

随机推荐

  1. 菜鸟初识AIDL进程通信
  2. [置顶] Android之高仿手机QQ聊天
  3. Android VCard通讯录导入导出详解
  4. Android自定义控件一简介
  5. 《第一行代码》学习笔记一
  6. Android中Intent的显示和隐式使用
  7. 网络界面Android 网络连接--Wifi/3G
  8. 什么组成了Android应用程序?
  9. build.gradle配置参数详解
  10. 每天学习一个Android中的常用框架——12.