在 Android 上通过模拟 HTTP multipart/form-data 请求协议信息实现图片上传

通过构造基于 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):

Java代码  

  1. /** 
  2.  * 文件名称:UploadImage.java 
  3.  * 
  4.  * 版权信息:Apache License, Version 2.0 
  5.  * 
  6.  * 功能描述:实现图片文件上传。 
  7.  * 
  8.  * 创建日期:2011-5-10 
  9.  * 
  10.  * 作者:Bert Lee 
  11.  */  
  12.   
  13. /* 
  14.  * 修改历史: 
  15.  */  
  16. public class UploadImage {  
  17.     String multipart_form_data = "multipart/form-data";  
  18.     String twoHyphens = "--";  
  19.     String boundary = "****************fD4fH3gL0hK7aI6";    // 数据分隔符  
  20.     String lineEnd = System.getProperty("line.separator");    // The value is "\r\n" in Windows.  
  21.       
  22.     /* 
  23.      * 上传图片内容,格式请参考HTTP 协议格式。 
  24.      * 人人网Photos.upload中的”程序调用“http://wiki.dev.renren.com/wiki/Photos.upload#.E7.A8.8B.E5.BA.8F.E8.B0.83.E7.94.A8 
  25.      * 对其格式解释的非常清晰。 
  26.      * 格式如下所示: 
  27.      * --****************fD4fH3hK7aI6 
  28.      * Content-Disposition: form-data; name="upload_file"; filename="apple.jpg" 
  29.      * Content-Type: image/jpeg 
  30.      * 
  31.      * 这儿是文件的内容,二进制流的形式 
  32.      */  
  33.     private void addImageContent(Image[] files, DataOutputStream output) {  
  34.         for(Image file : files) {  
  35.             StringBuilder split = new StringBuilder();  
  36.             split.append(twoHyphens + boundary + lineEnd);  
  37.             split.append("Content-Disposition: form-data; name=\"" + file.getFormName() + "\"; filename=\"" + file.getFileName() + "\"" + lineEnd);  
  38.             split.append("Content-Type: " + file.getContentType() + lineEnd);  
  39.             split.append(lineEnd);  
  40.             try {  
  41.                 // 发送图片数据  
  42.                 output.writeBytes(split.toString());  
  43.                 output.write(file.getData(), 0, file.getData().length);  
  44.                 output.writeBytes(lineEnd);  
  45.             } catch (IOException e) {  
  46.                 throw new RuntimeException(e);  
  47.             }  
  48.         }  
  49.     }  
  50.       
  51.     /* 
  52.      * 构建表单字段内容,格式请参考HTTP 协议格式(用FireBug可以抓取到相关数据)。(以便上传表单相对应的参数值) 
  53.      * 格式如下所示: 
  54.      * --****************fD4fH3hK7aI6 
  55.      * Content-Disposition: form-data; name="action" 
  56.      * // 一空行,必须有 
  57.      * upload 
  58.      */  
  59.     private void addFormField(Set<Map.Entry<Object,Object>> params, DataOutputStream output) {  
  60.         StringBuilder sb = new StringBuilder();  
  61.         for(Map.Entry<Object, Object> param : params) {  
  62.             sb.append(twoHyphens + boundary + lineEnd);  
  63.             sb.append("Content-Disposition: form-data; name=\"" + param.getKey() + "\"" + lineEnd);  
  64.             sb.append(lineEnd);  
  65.             sb.append(param.getValue() + lineEnd);  
  66.         }  
  67.         try {  
  68.             output.writeBytes(sb.toString());// 发送表单字段数据  
  69.         } catch (IOException e) {  
  70.             throw new RuntimeException(e);  
  71.         }  
  72.     }  
  73.       
  74.     /** 
  75.      * 直接通过 HTTP 协议提交数据到服务器,实现表单提交功能。 
  76.      * @param actionUrl 上传路径 
  77.      * @param params 请求参数key为参数名,value为参数值 
  78.      * @param files 上传文件信息 
  79.      * @return 返回请求结果 
  80.      */  
  81.     public String post(String actionUrl, Set<Map.Entry<Object,Object>> params, Image[] files) {  
  82.         HttpURLConnection conn = null;  
  83.         DataOutputStream output = null;  
  84.         BufferedReader input = null;  
  85.         try {  
  86.             URL url = new URL(actionUrl);  
  87.             conn = (HttpURLConnection) url.openConnection();  
  88.             conn.setConnectTimeout(120000);  
  89.             conn.setDoInput(true);        // 允许输入  
  90.             conn.setDoOutput(true);        // 允许输出  
  91.             conn.setUseCaches(false);    // 不使用Cache  
  92.             conn.setRequestMethod("POST");  
  93.             conn.setRequestProperty("Connection", "keep-alive");  
  94.             conn.setRequestProperty("Content-Type", multipart_form_data + "; boundary=" + boundary);  
  95.               
  96.             conn.connect();  
  97.             output = new DataOutputStream(conn.getOutputStream());  
  98.               
  99.             addImageContent(files, output);    // 添加图片内容  
  100.               
  101.             addFormField(params, output);    // 添加表单字段内容  
  102.               
  103.             output.writeBytes(twoHyphens + boundary + twoHyphens + lineEnd);// 数据结束标志  
  104.             output.flush();  
  105.               
  106.             int code = conn.getResponseCode();  
  107.             if(code != 200) {  
  108.                 throw new RuntimeException("请求‘" + actionUrl +"’失败!");  
  109.             }  
  110.               
  111.             input = new BufferedReader(new InputStreamReader(conn.getInputStream()));  
  112.             StringBuilder response = new StringBuilder();  
  113.             String oneLine;  
  114.             while((oneLine = input.readLine()) != null) {  
  115.                 response.append(oneLine + lineEnd);  
  116.             }  
  117.               
  118.             return response.toString();  
  119.         } catch (IOException e) {  
  120.             throw new RuntimeException(e);  
  121.         } finally {  
  122.             // 统一释放资源  
  123.             try {  
  124.                 if(output != null) {  
  125.                     output.close();  
  126.                 }  
  127.                 if(input != null) {  
  128.                     input.close();  
  129.                 }  
  130.             } catch (IOException e) {  
  131.                 throw new RuntimeException(e);  
  132.             }  
  133.               
  134.             if(conn != null) {  
  135.                 conn.disconnect();  
  136.             }  
  137.         }  
  138.     }  
  139.       
  140.     public static void main(String[] args) {  
  141.         try {  
  142.             String response = "";  
  143.               
  144.             BufferedReader in = new BufferedReader(new FileReader("config/actionUrl.properties"));  
  145.             String actionUrl = in.readLine();  
  146.               
  147.             // 读取表单对应的字段名称及其值  
  148.             Properties formDataParams = new Properties();  
  149.             formDataParams.load(new FileInputStream(new File("config/formDataParams.properties")));  
  150.             Set<Map.Entry<Object,Object>> params = formDataParams.entrySet();  
  151.               
  152.             // 读取图片所对应的表单字段名称及图片路径  
  153.             Properties imageParams = new Properties();  
  154.             imageParams.load(new FileInputStream(new File("config/imageParams.properties")));  
  155.             Set<Map.Entry<Object,Object>> images = imageParams.entrySet();  
  156.             Image[] files = new Image[images.size()];  
  157.             int i = 0;  
  158.             for(Map.Entry<Object,Object> image : images) {  
  159.                 Image file = new Image(image.getValue().toString(), image.getKey().toString());  
  160.                 files[i++] = file;  
  161.             }  
  162. //            Image file = new Image("images/apple.jpg", "upload_file");  
  163. //            Image[] files = new Image[0];  
  164. //            files[0] = file;  
  165.               
  166.             response = new UploadImage().post(actionUrl, params, files);  
  167.             System.out.println("返回结果:" + response);  
  168.         } catch (IOException e) {  
  169.             e.printStackTrace();  
  170.         }  
  171.     }  
  172. }  

2. 基于 HttpClient 框架实现文件上传,实例代码如下:

Java代码  

  1. /** 
  2.  * 文件名称:ClientMultipartFormPost.java 
  3.  * 
  4.  * 版权信息:Apache License, Version 2.0 
  5.  * 
  6.  * 功能描述:通过 HttpClient 4.1.1 实现文件上传。 
  7.  * 
  8.  * 创建日期:2011-5-15 
  9.  * 
  10.  * 作者:Bert Lee 
  11.  */  
  12.   
  13. /* 
  14.  * 修改历史: 
  15.  */  
  16. public class ClientMultipartFormPost {  
  17.     /** 
  18.      * 直接通过 HttpMime's MultipartEntity 提交数据到服务器,实现表单提交功能。 
  19.      * @return Post 请求所返回的内容 
  20.      */  
  21.     public static String filePost() {  
  22.         HttpClient httpclient = new DefaultHttpClient();  
  23.           
  24.         try {  
  25.             BufferedReader in = new BufferedReader(new FileReader("config/actionUrl.properties"));  
  26.             String actionUrl;  
  27.             actionUrl = in.readLine();  
  28.             HttpPost httppost = new HttpPost(actionUrl);  
  29.               
  30.             // 通过阅读源码可知,要想实现图片上传功能,必须将 MultipartEntity 的模式设置为 BROWSER_COMPATIBLE 。  
  31.             MultipartEntity multiEntity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE);  
  32. //            MultipartEntity multiEntity = new MultipartEntity();  
  33.               
  34.             // 读取图片的 MIME Type 类型集  
  35.             Properties mimeTypes = new Properties();  
  36.             mimeTypes.load(new FileInputStream(new File("config/MIMETypes.properties")));  
  37.               
  38.             // 构造图片数据  
  39.             Properties imageParams = new Properties();  
  40.             imageParams.load(new FileInputStream(new File("config/imageParams.properties")));  
  41.             String fileType;  
  42.             for(Map.Entry<Object,Object> image : imageParams.entrySet()) {  
  43.                 String path = image.getValue().toString();  
  44.                 fileType = path.substring(path.lastIndexOf(".") + 1);  
  45.                 FileBody binaryContent = new FileBody(new File(path), mimeTypes.get(fileType).toString());  
  46. //                FileBody binaryContent = new FileBody(new File(path));  
  47.                 multiEntity.addPart(image.getKey().toString(), binaryContent);  
  48.             }  
  49.               
  50.             // 构造表单参数数据  
  51.             Properties formDataParams = new Properties();  
  52.             formDataParams.load(new FileInputStream(new File("config/formDataParams.properties")));  
  53.             for(Entry<Object, Object> param : formDataParams.entrySet()) {  
  54.                 multiEntity.addPart(param.getKey().toString(), new StringBody(param.getValue().toString()));  
  55.             }  
  56.               
  57.             httppost.setEntity(multiEntity);  
  58. //            Out.println("executing request " + httppost.getRequestLine());  
  59.               
  60.             HttpResponse response = httpclient.execute(httppost);  
  61.             HttpEntity resEntity = response.getEntity();  
  62.               
  63. //            Out.println("-------------------");  
  64. //            Out.println(response.getStatusLine());  
  65.             if(resEntity != null) {  
  66.                 String returnContent = EntityUtils.toString(resEntity);  
  67.                 EntityUtils.consume(resEntity);  
  68.                   
  69.                 return returnContent; // 返回页面内容  
  70.             }  
  71.         } catch (IOException e) {  
  72.             e.printStackTrace();  
  73.         } finally {  
  74.             // 释放资源  
  75.             httpclient.getConnectionManager().shutdown();  
  76.         }  
  77.         return null;  
  78.     }  
  79.   
  80.     // 测试  
  81.     public static void main(String[] args) {  
  82.         Out.println("Response content: " + ClientMultipartFormPost.filePost());  
  83.     }  
  84.   
  85. }  

 

 

参考资料:

时间: 2024-10-18 13:12:17

在 Android 上通过模拟 HTTP multipart/form-data 请求协议信息实现图片上传的相关文章

jsp标签-将图书的信息及图片上传至数据库并显示

问题描述 将图书的信息及图片上传至数据库并显示 jsp怎么办?急用求救将图书的信息及图片上传至数据库并显示 解决方案 http://blog.163.com/greencoat_man@126/blog/static/10261923520090147253890/http://www.cnblogs.com/long/archive/2005/06/02/166546.htmlhttp://wineer200.iteye.com/blog/404303

POST请求,iOS开发时,图片上传的时候,类型是文件流,怎么写

问题描述 POST请求,iOS开发时,图片上传的时候,类型是文件流,怎么写 POST请求,iOS开发时,图片上传的时候,类型是文件流,怎么写,在线等 解决方案 http://blog.csdn.net/zhangkongzhongyun/article/details/8277426 解决方案二: 给个思路就行,或者给个链接

Drupal7通过form API 建立无刷新的图片上传功能的四个方法

表单是网站常用的,不可缺少的.而通过表单建立图片上传也是刚需,基本每个网站都需要图片上传功能,现在比较流行的是直接无刷新上传图片,无刷新上传图片在drupal7 如何做呢?下面代码就是实现此功能. 方法1:用file原件配合ajax参数实现: function testmodule_forma($form, &$form_state){ $form['im-container'] = array(     '#prefix'=>'<div id="im-area"&

php 图片上传并预览效果

if(!isset($_session))session_start(); /**2010-6-22  * $data 数组类型    包含以下变量  -------------------------------  * $sub_type        submit类型(upload:上传按钮    delete:删除按钮),默认upload  * $file            通过表单获取的$_files['filename']数组  * $img_tag_id        预览图片的

ruby on rails爬坑图片上传及显示的实例

一,问题及思路 最近在用 rails + react + mysql 基本框架写一个cms + client的项目,里面涉及到了图片的上传及显示,下面简单说说思路,至于这个项目的配置部署,应该会在寒假结束总结分享一下. rails中图片上传及显示要解决主要问题是: 图片存在哪? 图片格式大小? 客户端怎么显示图片? 因为这是个小项目,估计最多1000张图片,最多占用空间1G,所以采取相对简便的方法: 图片保存在rails的public文件夹里(也就是保存在部署该项目的主机中) ,如果图片比较多的

android实现图片上传功能(springMvc)

本文实例为大家分享了Android图片上传的具体代码,供大家参考,具体内容如下 Android端: String fileName = tvFilename.getText().toString(); RequestBody description = RequestBody.create( okhttp3.MultipartBody.FORM, fileName); File fileImage = new File(saveFileName); RequestBody requestBody

android选择图片或拍照图片上传到服务器(包括上传参数)

http://blog.csdn.net/vipa1888/article/details/8213898 最近要搞一个项目,需要上传相册和拍照的图片,不负所望,终于完成了!  不过需要说明一下,其实网上很多教程拍照的图片,都是缩略图不是很清晰,所以需要在调用照相机的时候,事先生成一个地址,用于标识拍照的图片URI 具体上传代码: 1.选择图片和上传界面,包括上传完成和异常的回调监听 [java] view plaincopy package com.spring.sky.image.uploa

使用jquery.form.js实现图片上传的方法_jquery

本文实例讲述了使用jquery.form.js实现图片上传的方法.分享给大家供大家参考,具体如下: testupfile2.php <?php header('Content-type:text/html;charset=utf-8'); include_once 'includes/common.inc.php'; if(!empty($_FILES['upfile'])){ //文件格式 $image=array('image/jpg', 'image/jpeg', 'image/png',

Android用HandlerThread模拟AsyncTask功能(ThreadTask)_Android

前言  AsyncTask是个好东西,能处理绝大多数应用线程和更新UI的任务,由于其内部使用了静态线程池,如果你有一堆异步任务(例如全局定时更新数据.同一个Activity中多个AsyncTask同时执行)其中有不能马上执行完的情况(例如网络请求超时),那就糟了,其他任务都还等着呢,就会出现任务卡住的情况.此时就需要直接上Thread了,这里参考AsyncTask的API封装了一个ThreadTask,便于必要时代码替换,欢迎交流!  正文实例代码: import android.os.Hand