在 Android 上透过模拟 HTTP multipart/form-data 请求协议信息实现图片上传
在 Android 上通过模拟 HTTP multipart/form-data 请求协议信息实现图片上传http://blog.csdn.net/jdsjlzx
在 Android 上通过模拟 HTTP multipart/form-data 请求协议信息实现图片上传
http://blog.csdn.net/jdsjlzx/article/details/8635884
通过构造基于 HTTP 协议的传输内容实现图片自动上传到服务器功能 。如果自己编码构造 HTTP 协议,那么编写的代码质量肯定不高,建议模仿?HttpClient?.zip?examples\mime\ClientMultipartFormPost.java 来实现,并通过源码来进一步理解如何优雅高效地构造 HTTP 协议传输内容。
?
?
?
自己构造 HTTP 协议传输内容的想法,从何而来呢?灵感启迪于这篇博文“Android下的应用编程——用HTTP协议实现文件上传功能?”,以前从未想过通过抓取 HTTP 请求数据格式,根据协议自己构造数据来实现数据提交。哎,Out 了。因为 Apache HttpClient 框架就是通过此方式来实现的,以前从未注意到,看来以后要多多向前人学习啊!结 果是:阅读了此框架的源码后,才知道自己编写的代码和人家相比真不是一个档次的。现在已经下定决心了,多读开源框架代码,不但可以熟悉相关业务流程,而且 还可以学到设计模式在实际业务需求中的应用,更重要的是领悟其中的思想。业务流程、实践能力、框架思想,一举三得,何乐而不为呢。^_^
?
?
?
test.html 部分源码:
?
<form action="Your_Action_Url?" method="post" enctype="multipart/form-data?" name="form1" id="form1">
? <p>
??? <label for="upload_file"></label>
??? <input type="file" name="upload_file" id="upload_file?" />
? </p>
? <p>
??? <input type="submit" name="action" id="action?" value="upload?" />
? </p>
</form>
?
通过 HttpWatch 查看抓取到的包数据格式:
?
?
?
?
下面将分别通过按照 HttpWatch 抓取下来的协议格式内容构造传输内容实现文件上传功能和基于 HttpClient 框架实现文件上传功能。
?
项目配置目录Your_Project/config?,相关文件?如下:
?
actionUrl.properties 文件内容:
?
Your_Action_Url
?
formDataParams.properties 文件内容(对应 HTML Form 属性内容):
?
action?=upload
?
imageParams.properties 文件内容(这里文件路径已配置死了,不好!建议在程序中动态设置,即通过传入相关参数实现。):
?
upload_file?=images/roewe.jpg
?
MIMETypes.properties 文件内容(参考自?Multimedia MIME?Reference?):
?
jpeg:image/jpeg
jpg:image/jpeg
png:image/png
gif:image/gif
?
?
1. 在《Android下的应用编程——用HTTP协议实现文件上传功能?》代码的基础上,通过进一步改进得到如下代码(Java、Android 都可以 run):
?
- /**?
- ?*?文件名称:UploadImage.java?
- ?*?
- ?*?版权信息:Apache?License,?Version?2.0?
- ?*?
- ?*?功能描述:实现图片文件上传。?
- ?*?
- ?*?创建日期:2011-5-10?
- ?*?
- ?*?作者:Bert?Lee?
- ?*/??
- ??
- /*?
- ?*?修改历史:?
- ?*/??
- public?class?UploadImage?{??
- ????String?multipart_form_data?=?"multipart/form-data";??
- ????String?twoHyphens?=?"--";??
- ????String?boundary?=?"****************fD4fH3gL0hK7aI6";????//?数据分隔符??
- ????String?lineEnd?=?System.getProperty("line.separator");????//?The?value?is?"\r\n"?in?Windows.??
- ??????
- ????/*?
- ?????*?上传图片内容,格式请参考HTTP?协议格式。?
- ?????*?人人网Photos.upload中的”程序调用“http://wiki.dev.renren.com/wiki/Photos.upload#.E7.A8.8B.E5.BA.8F.E8.B0.83.E7.94.A8?
- ?????*?对其格式解释的非常清晰。?
- ?????*?格式如下所示:?
- ?????*?--****************fD4fH3hK7aI6?
- ?????*?Content-Disposition:?form-data;?name="upload_file";?filename="apple.jpg"?
- ?????*?Content-Type:?image/jpeg?
- ?????*?
- ?????*?这儿是文件的内容,二进制流的形式?
- ?????*/??
- ????private?void?addImageContent(Image[]?files,?DataOutputStream?output)?{??
- ????????for(Image?file?:?files)?{??
- ????????????StringBuilder?split?=?new?StringBuilder();??
- ????????????split.append(twoHyphens?+?boundary?+?lineEnd);??
- ????????????split.append("Content-Disposition:?form-data;?name=""?+?file.getFormName()?+?"";?filename=""?+?file.getFileName()?+?"""?+?lineEnd);??
- ????????????split.append("Content-Type:?"?+?file.getContentType()?+?lineEnd);??
- ????????????split.append(lineEnd);??
- ????????????try?{??
- ????????????????//?发送图片数据??
- ????????????????output.writeBytes(split.toString());??
- ????????????????output.write(file.getData(),?0,?file.getData().length);??
- ????????????????output.writeBytes(lineEnd);??
- ????????????}?catch?(IOException?e)?{??
- ????????????????throw?new?RuntimeException(e);??
- ????????????}??
- ????????}??
- ????}??
- ??????
- ????/*?
- ?????*?构建表单字段内容,格式请参考HTTP?协议格式(用FireBug可以抓取到相关数据)。(以便上传表单相对应的参数值)?
- ?????*?格式如下所示:?
- ?????*?--****************fD4fH3hK7aI6?
- ?????*?Content-Disposition:?form-data;?name="action"?
- ?????*?//?一空行,必须有?
- ?????*?upload?
- ?????*/??
- ????private?void?addFormField(Set<Map.Entry<Object,Object>>?params,?DataOutputStream?output)?{??
- ????????StringBuilder?sb?=?new?StringBuilder();??
- ????????for(Map.Entry<Object,?Object>?param?:?params)?{??
- ????????????sb.append(twoHyphens?+?boundary?+?lineEnd);??
- ????????????sb.append("Content-Disposition:?form-data;?name=""?+?param.getKey()?+?"""?+?lineEnd);??
- ????????????sb.append(lineEnd);??
- ????????????sb.append(param.getValue()?+?lineEnd);??
- ????????}??
- ????????try?{??
- ????????????output.writeBytes(sb.toString());//?发送表单字段数据??
- ????????}?catch?(IOException?e)?{??
- ????????????throw?new?RuntimeException(e);??
- ????????}??
- ????}??
- ??????
- ????/**?
- ?????*?直接通过?HTTP?协议提交数据到服务器,实现表单提交功能。?
- ?????*?@param?actionUrl?上传路径?
- ?????*?@param?params?请求参数key为参数名,value为参数值?
- ?????*?@param?files?上传文件信息?
- ?????*?@return?返回请求结果?
- ?????*/??
- ????public?String?post(String?actionUrl,?Set<Map.Entry<Object,Object>>?params,?Image[]?files)?{??
- ????????HttpURLConnection?conn?=?null;??
- ????????DataOutputStream?output?=?null;??
- ????????BufferedReader?input?=?null;??
- ????????try?{??
- ????????????URL?url?=?new?URL(actionUrl);??
- ????????????conn?=?(HttpURLConnection)?url.openConnection();??
- ????????????conn.setConnectTimeout(120000);??
- ????????????conn.setDoInput(true);????????//?允许输入??
- ????????????conn.setDoOutput(true);????????//?允许输出??
- ????????????conn.setUseCaches(false);????//?不使用Cache??
- ????????????conn.setRequestMethod("POST");??
- ????????????conn.setRequestProperty("Connection",?"keep-alive");??
- ????????????conn.setRequestProperty("Content-Type",?multipart_form_data?+?";?boundary="?+?boundary);??
- ??????????????
- ????????????conn.connect();??
- ????????????output?=?new?DataOutputStream(conn.getOutputStream());??
- ??????????????
- ????????????addImageContent(files,?output);????//?添加图片内容??
- ??????????????
- ????????????addFormField(params,?output);????//?添加表单字段内容??
- ??????????????
- ????????????output.writeBytes(twoHyphens?+?boundary?+?twoHyphens?+?lineEnd);//?数据结束标志??
- ????????????output.flush();??
- ??????????????
- ????????????int?code?=?conn.getResponseCode();??
- ????????????if(code?!=?200)?{??
- ????????????????throw?new?RuntimeException("请求‘"?+?actionUrl?+"’失败!");??
- ????????????}??
- ??????????????
- ????????????input?=?new?BufferedReader(new?InputStreamReader(conn.getInputStream()));??
- ????????????StringBuilder?response?=?new?StringBuilder();??
- ????????????String?oneLine;??
- ????????????while((oneLine?=?input.readLine())?!=?null)?{??
- ????????????????response.append(oneLine?+?lineEnd);??
- ????????????}??
- ??????????????
- ????????????return?response.toString();??
- ????????}?catch?(IOException?e)?{??
- ????????????throw?new?RuntimeException(e);??
- ????????}?finally?{??
- ????????????//?统一释放资源??
- ????????????try?{??
- ????????????????if(output?!=?null)?{??
- ????????????????????output.close();??
- ????????????????}??
- ????????????????if(input?!=?null)?{??
- ????????????????????input.close();??
- ????????????????}??
- ????????????}?catch?(IOException?e)?{??
- ????????????????throw?new?RuntimeException(e);??
- ????????????}??
- ??????????????
- ????????????if(conn?!=?null)?{??
- ????????????????conn.disconnect();??
- ????????????}??
- ????????}??
- ????}??
- ??????
- ????public?static?void?main(String[]?args)?{??
- ????????try?{??
- ????????????String?response?=?"";??
- ??????????????
- ????????????BufferedReader?in?=?new?BufferedReader(new?FileReader("config/actionUrl.properties"));??
- ????????????String?actionUrl?=?in.readLine();??
- ??????????????
- ????????????//?读取表单对应的字段名称及其值??
- ????????????Properties?formDataParams?=?new?Properties();??
- ????????????formDataParams.load(new?FileInputStream(new?File("config/formDataParams.properties")));??
- ????????????Set<Map.Entry<Object,Object>>?params?=?formDataParams.entrySet();??
- ??????????????
- ????????????//?读取图片所对应的表单字段名称及图片路径??
- ????????????Properties?imageParams?=?new?Properties();??
- ????????????imageParams.load(new?FileInputStream(new?File("config/imageParams.properties")));??
- ????????????Set<Map.Entry<Object,Object>>?images?=?imageParams.entrySet();??
- ????????????Image[]?files?=?new?Image[images.size()];??
- ????????????int?i?=?0;??
- ????????????for(Map.Entry<Object,Object>?image?:?images)?{??
- ????????????????Image?file?=?new?Image(image.getValue().toString(),?image.getKey().toString());??
- ????????????????files[i++]?=?file;??
- ????????????}??
- //????????????Image?file?=?new?Image("images/apple.jpg",?"upload_file");??
- //????????????Image[]?files?=?new?Image[0];??
- //????????????files[0]?=?file;??
- ??????????????
- ????????????response?=?new?UploadImage().post(actionUrl,?params,?files);??
- ????????????System.out.println("返回结果:"?+?response);??
- ????????}?catch?(IOException?e)?{??
- ????????????e.printStackTrace();??
- ????????}??
- ????}??
- }?
2. 基于 HttpClient 框架实现文件上传,实例代码如下:
?
- /**?
- ?*?文件名称:ClientMultipartFormPost.java?
- ?*?
- ?*?版权信息:Apache?License,?Version?2.0?
- ?*?
- ?*?功能描述:通过?HttpClient?4.1.1?实现文件上传。?
- ?*?
- ?*?创建日期:2011-5-15?
- ?*?
- ?*?作者:Bert?Lee?
- ?*/??
- ??
- /*?
- ?*?修改历史:?
- ?*/??
- public?class?ClientMultipartFormPost?{??
- ????/**?
- ?????*?直接通过?HttpMime's?MultipartEntity?提交数据到服务器,实现表单提交功能。?
- ?????*?@return?Post?请求所返回的内容?
- ?????*/??
- ????public?static?String?filePost()?{??
- ????????HttpClient?httpclient?=?new?DefaultHttpClient();??
- ??????????
- ????????try?{??
- ????????????BufferedReader?in?=?new?BufferedReader(new?FileReader("config/actionUrl.properties"));??
- ????????????String?actionUrl;??
- ????????????actionUrl?=?in.readLine();??
- ????????????HttpPost?httppost?=?new?HttpPost(actionUrl);??
- ??????????????
- ????????????//?通过阅读源码可知,要想实现图片上传功能,必须将?MultipartEntity?的模式设置为?BROWSER_COMPATIBLE?。??
- ????????????MultipartEntity?multiEntity?=?new?MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE);??
- //????????????MultipartEntity?multiEntity?=?new?MultipartEntity();??
- ??????????????
- ????????????//?读取图片的?MIME?Type?类型集??
- ????????????Properties?mimeTypes?=?new?Properties();??
- ????????????mimeTypes.load(new?FileInputStream(new?File("config/MIMETypes.properties")));??
- ??????????????
- ????????????//?构造图片数据??
- ????????????Properties?imageParams?=?new?Properties();??
- ????????????imageParams.load(new?FileInputStream(new?File("config/imageParams.properties")));??
- ????????????String?fileType;??
- ????????????for(Map.Entry<Object,Object>?image?:?imageParams.entrySet())?{??
- ????????????????String?path?=?image.getValue().toString();??
- ????????????????fileType?=?path.substring(path.lastIndexOf(".")?+?1);??
- ????????????????FileBody?binaryContent?=?new?FileBody(new?File(path),?mimeTypes.get(fileType).toString());??
- //????????????????FileBody?binaryContent?=?new?FileBody(new?File(path));??
- ????????????????multiEntity.addPart(image.getKey().toString(),?binaryContent);??
- ????????????}??
- ??????????????
- ????????????//?构造表单参数数据??
- ????????????Properties?formDataParams?=?new?Properties();??
- ????????????formDataParams.load(new?FileInputStream(new?File("config/formDataParams.properties")));??
- ????????????for(Entry<Object,?Object>?param?:?formDataParams.entrySet())?{??
- ????????????????multiEntity.addPart(param.getKey().toString(),?new?StringBody(param.getValue().toString()));??
- ????????????}??
- ??????????????
- ????????????httppost.setEntity(multiEntity);??
- //????????????Out.println("executing?request?"?+?httppost.getRequestLine());??
- ??????????????
- ????????????HttpResponse?response?=?httpclient.execute(httppost);??
- ????????????HttpEntity?resEntity?=?response.getEntity();??
- ??????????????
- //????????????Out.println("-------------------");??
- //????????????Out.println(response.getStatusLine());??
- ????????????if(resEntity?!=?null)?{??
- ????????????????String?returnContent?=?EntityUtils.toString(resEntity);??
- ????????????????EntityUtils.consume(resEntity);??
- ??????????????????
- ????????????????return?returnContent;?//?返回页面内容??
- ????????????}??
- ????????}?catch?(IOException?e)?{??
- ????????????e.printStackTrace();??
- ????????}?finally?{??
- ????????????//?释放资源??
- ????????????httpclient.getConnectionManager().shutdown();??
- ????????}??
- ????????return?null;??
- ????}??
- ??
- ????//?测试??
- ????public?static?void?main(String[]?args)?{??
- ????????Out.println("Response?content:?"?+?ClientMultipartFormPost.filePost());??
- ????}??
- ??
- }?
?