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

news/2024/7/7 12:36:22

 

通过构造基于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代码 

/** 

 * 文件名称: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();  

        }  

    }  

 

/**

 * 文件名称: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 框架实现文件上传,实例代码如下:

 

Java代码 

/** 

 * 文件名称: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());  

    }  

 

 

/**

 * 文件名称: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());

    }

 

}   





http://www.niftyadmin.cn/n/3650436.html

相关文章

如何在Ubuntu 18.04上设置Apache虚拟主机[快速入门]

介绍 (Introduction) This tutorial will guide you through setting up multiple domains and websites using Apache virtual hosts on an Ubuntu 18.04 server. During this process, you’ll learn how to serve different content to different visitors depending on whi…

看Borland IDE向何处去

《乌鸦FANS》再次灵验&#xff0c;今天得到消息称《Borland欲出售IDE部门》&#xff0c;NASDAQ上的BORL股价应声下跌&#xff08;下图为5日走势图&#xff0c;来自NASDAQ&#xff09;&#xff0c;但我觉得这对Borland用户来说&#xff0c;应该是个利好。李维就此写了一篇《“Bo…

Android下的应用编程——用HTTP协议实现文件上传功能

【文章作者】曾健生 【作者邮箱】zengjiansheng1126.com 【作者QQ】190678908 【作者MSN】zengjiansheng1hotmail.com 【作者博客】blog.csdn.net/newjueqi ******************************************************************************* 在Android的客户端编程中&…

如何将Redis数据迁移到DigitalOcean托管数据库

介绍 (Introduction) There are a number of methods you can use to migrate data from one Redis instance to another, such as replication or snapshotting. However, migrations can get more complicated when you’re moving data to a Redis instance managed by a cl…

两种不同的Web应用

对于今天火炬说Donews Blog将换用WordPress的事后&#xff0c;令狐提出了一个技术方面的问题&#xff0c;我们就此讨论了一番&#xff1a;令狐:我倒是不关心WP好不好&#xff0c;而是觉得一些人&#xff08;也许包括我&#xff09;对于“Web应用”这一概念是不是应该反思一下了…

jest测试react组件_如何使用Jest为React组件编写快照测试

jest测试react组件In this tutorial, we will be looking at what snapshot tests are and how we can use snapshot testing to ensure our user interface does not change without the team knowing about it. 在本教程中&#xff0c;我们将研究什么是快照测试以及如何使用快…

搭建支持 OAuth Echo 的第三方 twitter 应用

背景&#xff1a;进入8月中&#xff0c;twitter 已经在每天减少 Basic Auth 的 API limit了&#xff0c;到月底 Basic Auth 将彻底关闭&#xff0c;也就是说到月底&#xff0c;所有的客户端&#xff0c;twitter与第三方服务之间都必须使用 OAuth 来传递帐号密码等信息。 我喜欢…

SVN+HTTP的一个ulgy的错误

原来配置得好好的基于HTTP的SVN忽然就出问题了&#xff0c;新增加的文件都加不上&#xff0c;一提交就出一个什么&#xff1a;302 Found的错误。在Google上找了半天才找到&#xff0c;竟然是这么个ugly的错误。The solution is to disable special 404 error handling for Subv…