Android学习第十九天----post请求数据解析

HTTP协议中GET、POST和HEAD的介绍
GET: 请求指定的页面信息,并返回实体主体。
HEAD: 只请求页面的首部。
POST: 请求服务器接受所指定的文档作为对所标识的URI的新的从属实体。

  HTTP 定义了与服务器交互的不同方法,最基本的方法是 GET 和 POST。事实上 GET 适用于多数请求,而保留 POST仅用于更新站点。根据 HTTP 规范,GET 用于信息获取,而且应该是安全的和幂等的。所谓安全的意味着该操作用于获取信息而非修改信息。换句话说,GET 请求一般不应产生副作用。幂等的意味着对同一 URL的多个请求应该返回同样的结果。完整的定义并不像看起来那样严格。从根本上讲,其目标是当用户打开一个链接时,她可以确信从自身的角度来看没有改变资源。

  比如,新闻站点的头版不断更新。虽然第二次请求会返回不同的一批新闻,该操作仍然被认为是安全的和幂等的,因为它总是返回当前的新闻。反之亦然。POST请求就不那么轻松了。POST 表示可能改变服务器上的资源的请求。仍然以新闻站点为例,读者对文章的注解应该通过 POST请求实现,因为在注解提交之后站点已经不同了(比方说文章下面出现一条注解);在FORM提交的时候,如果不指定Method,则默认为GET请 求,Form中提交的数据将会附加在url之后,以?分开与url分开。字母数字字符原样发送,但空格转为“+“号,[url=javascript:;]其它[/url]符号转换为%XX,其中XX为 该符号以16进制表示的ASCII(或ISO Latin-1)值。GET请求请提交的数据放置在HTTP请求协议头中,而POST提交的数据则放在实体数据中;GET方式提交的数据最多只能有1024字节,而POST则没有此限制。 

  在表单里使用”post”和”get”有什么区别
  在Form里面,可以使用post也可以使用get。它们都是method的合法取值。但是,post和get方法在使用上至少有两点不同:
  1、Get方法通过URL请求来传递用户的输入。Post方法通过另外的形式。
  2、Get方式的提交需要用Request.QueryString来取得变量的值,而Post方式提交时,你必须通过Request.Form来访问提交的内容。

通过get方法提交数据,可能会带来安全性的问题。比如一个登陆页面。当通过get方法提交数据时,用户名和密码将出现在URL上。如果:
1、 登陆页面可以被浏览器缓存;
2、 [url=javascript:;]其他[/url]

人可以访问客户的这台机器。
那么,别人即可以从浏览器的历史记录中,读取到此客户的账号和密码。所以,在某些情况下,get方法会带来严重的安全性问题。
建议
--------------------------------------------------------------------------------------------------------------------
GET: 请求指定的页面信息,并返回实体主体。
HEAD: 只请求页面的首部。
POST: 请求服务器接受所指定的文档作为对所标识的URI的新的从属实体。
PUT: 从客户端向服务器传送的数据取代指定的文档的内容。
DELETE: 请求服务器删除指定的页面。
OPTIONS: 允许客户端查看服务器的性能。
TRACE: 请求服务器在响应中的实体主体部分返回所得到的内容。
PATCH: 实体中包含一个表,表中说明与该URI所表示的原内容的区别。
MOVE: 请求服务器将指定的页面移至另一个网络地址。
COPY: 请求服务器将指定的页面拷贝至另一个网络地址。
LINK: 请求服务器建立链接关系。
UNLINK: 断开链接关系。
WRAPPED: 允许客户端发送经过封装的请求。
Extension-mothed:在不改动协议的前提下,可增加另外的方法。
比如:
GET /index.html HTTP/1.1
Accept: text/plain /*纯ASCII码文本文件*/
Accept: text/html /*HTML文本文件*/
User-Agent:Mozilla/4.5(WinNT)
说明浏览器使用Get方法请求文档/index.html。浏览器则只允许接收纯ASCII码文本文件和HTML文本文件,其使用的引擎是Mozilla/4.5(Netscape)。
当服务器响应时,其状态行的信息为HTTP的版本号,状态码,及解释状态码的简单说明。现将5类状态码详细列出:
① 客户方错误
100  继续
101  交换协议
② 成功
200  OK
201  已创建
202  接收
203  非认证信息
204  无内容
205  重置内容
206  部分内容
③ 重定向
300  多路选择
301  永久转移
302  暂时转移
303  参见其它
304  未修改(Not Modified)
305  使用代理
④ 客户方错误
400  错误请求(Bad Request)
401  未认证
402  需要付费
403  禁止(Forbidden)
404  未找到(Not Found)
405  方法不允许
406  不接受
407  需要代理认证
408  请求超时
409  冲突
410  失败
411  需要长度
412  条件失败
413  请求实体太大
414  请求URI太长
415  不支持媒体类型
⑤ 服务器错误
500  服务器内部错误
501  未实现(Not Implemented)
502  网关失败
504  网关超时
505 HTTP版本不支持
比如:(在《TELNET……》一文中用telnet登陆80端口,相同的方法用在HTTP/1.1中,会发现没有显示,下面补充说明之)

80
HEAD / HTTP/1.1
host:www.fudan.edu.cn /*本行为输入内容*/
HTTP/1.1 501 Method Not Implemented
Date: Web, 01 Nov 2000 07:12:29 GMT /*当前的日期/时间*/
Server: Apache/1.3.12 (Unix) /*Web服务器信息*/
Allow: GET, HEAD, OPTION, TRACE /*支持的方法类型*/
Connection: close
Connect-Type: Text/html; charset=iso-8859-1/*连接的媒体类型*/
501 Method
Not Implemented
Method Not Implemented
head to /inde
x.html not supported.
Invalid method in request head / htp/1.1
Apache/1.3.12 Server at
Port 80
关于实体头部的内容还可以有:
Last Modified :请求文档的最近修改时间。
Expires :请求文档的过期时间。
Connect-length:文档数据的长度。
WWW-authenricate:通知客户端需要的认证信息。
Connect-encoding :说明有无使用压缩技术。
Transfer-encoding :说明采用的编码变换类型。
随着Internet的发展,下一代的HTTP协议HTTP-ng已经在酝酿之中,它将会提供更好的安全性、更快的速度,其改进要点为:模块化强、网络效率高

以下是post请求的一个简单的例子,通过软件HttpWatch显示出来的:

POST /upload_file/UploadFile HTTP/1.1
Accept: text/plain, */*
Accept-Language: zh-cn
Host: 192.168.29.65:80
Content-Type:multipart/form-data;boundary=---------------------------7d33a816d302b6
User-Agent: Mozilla/4.0 (compatible; OpenOffice.org)
Content-Length: 424
Connection: Keep-Alive

-----------------------------7d33a816d302b6
Content-Disposition: form-data; name="userfile1"; filename="E:\s"
Content-Type: application/octet-stream
a
bb
XXX
ccc
-----------------------------7d33a816d302b6
Content-Disposition: form-data; name="text1"
foo
-----------------------------7d33a816d302b6
Content-Disposition: form-data; name="password1"
bar
-----------------------------7d33a816d302b6--

在android中如果想要向服务器端发送post请求,可以自己拼接这个字符串,然后向服务器发送该请求就可以完成post请求;

在xml中进行如下的布局:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >

        
   <TextView  
        android:layout_width="fill_parent" 
        android:layout_height="wrap_content" 
        android:text="文件名"
        />
    
    <EditText
        android:layout_width="fill_parent" 
        android:layout_height="wrap_content" 
        android:text="a2.png"
        android:id="@+id/filename"
        />
                
    <Button
        android:layout_width="wrap_content" 
        android:layout_height="wrap_content" 
        android:text="上传"
        android:id="@+id/button1"
        />
</LinearLayout>

需要将文件实体化,如以下所示:

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;

//对HTTP实体数据进行封装
public class FormFile {
    // 上传文件的数据 
    private byte[] data;
    private InputStream inStream;
    //上传的文件
    private File file;
    // 文件名称 
    private String filname;
    // 请求参数名称
    private String parameterName;
    // 内容类型 
    private String contentType = "application/octet-stream";
    //读取数据文件比较小的时候使用的构造方法
    public FormFile(String filname, byte[] data, String parameterName, String contentType) {
        this.data = data;
        this.filname = filname;
        this.parameterName = parameterName;
        if(contentType!=null) this.contentType = contentType;
    }
    //读取文件比较大时使用的构造方法
    public FormFile(String filname, File file, String parameterName, String contentType) {
        this.filname = filname;
        this.parameterName = parameterName;
        this.file = file;
        try {
            this.inStream = new FileInputStream(file);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        if(contentType!=null) this.contentType = contentType;
    }
    
    public File getFile() {
        return file;
    }

    public InputStream getInStream() {
        return inStream;
    }

    public byte[] getData() {
        return data;
    }

    public String getFilname() {
        return filname;
    }

    public void setFilname(String filname) {
        this.filname = filname;
    }

    public String getParameterName() {
        return parameterName;
    }

    public void setParameterName(String parameterName) {
        this.parameterName = parameterName;
    }

    public String getContentType() {
        return contentType;
    }

    public void setContentType(String contentType) {
        this.contentType = contentType;
    }
    
}

然后编写主要的httppost协议拼接类:

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.InetAddress;
import java.net.Socket;
import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;

public class SocketHttpRequester {

    /**
     * 整个操作务必观察换行符
     */
    public static boolean post(String path, Map<String, String> params,
        FormFile[] files) throws Exception 
        {
    // 数据分隔线
    final String BOUNDARY = "---------------------------7dd26c2a303b8";
    // 数据结束标志
    final String endline = "--" + BOUNDARY + "--\r\n";
    // 文件数据长度默认为0
    int fileDataLength = 0;

    // 得到文件类型数据的总长度 input type=file;
    for (FormFile uploadFile : files) 
    {
        StringBuilder fileExplain = new StringBuilder();
        fileExplain.append("--");
        fileExplain.append(BOUNDARY);
        fileExplain.append("\r\n");
        fileExplain.append("Content-Disposition: form-data;name=\""
            + uploadFile.getParameterName() + "\";filename=\""
            + uploadFile.getFilname() + "\"\r\n");
        fileExplain.append("Content-Type: " + uploadFile.getContentType()
            + "\r\n\r\n");
        fileExplain.append("\r\n");
        fileDataLength += fileExplain.length();
        // 根据io流判断用哪儿种方法读取文件
        if (uploadFile.getInStream() != null) {
        fileDataLength += uploadFile.getFile().length();
        } else {
        fileDataLength += uploadFile.getData().length;
        }
    }

    // 构造文本类型参数的实体数据 input type="text"
    StringBuilder textEntity = new StringBuilder();

    for (Map.Entry<String, String> entry : params.entrySet()) {
        textEntity.append("--");
        textEntity.append(BOUNDARY);
        textEntity.append("\r\n");
        textEntity.append("Content-Disposition: form-data; name=\""
            + entry.getKey() + "\"\r\n\r\n");
        textEntity.append(entry.getValue());
        textEntity.append("\r\n");
    }
    // 计算传输给服务器的实体数据总长度
    int dataLength = textEntity.toString().getBytes().length
        + fileDataLength + endline.getBytes().length;

    URL url = new URL(path);
    // 获得链接端口号,一般网站没有端口号那么它的端口号默认是80
    int port = url.getPort() == -1 ? 80 : url.getPort();
    // 利用SOCKET 来链接web服务器,httpurlconnection上传大量数据会内存溢出
    Socket socket = new Socket(InetAddress.getByName(url.getHost()), port);
    OutputStream outStream = socket.getOutputStream();
    // 设置HTTP请求头信息并且发送
    String requestmethod = "POST " + url.getPath() + " HTTP/1.1\r\n";
    outStream.write(requestmethod.getBytes());
    String accept = "Accept: image/gif, image/jpeg, image/pjpeg, image/pjpeg" +
            ", application/x-shockwave-flash, application/xaml+xml" +
            ", application/vnd.ms-xpsdocument, application/x-ms-xbap" +
            ", application/x-ms-application, application/vnd.ms-excel" +
            ", application/vnd.ms-powerpoint, application/msword, */*\r\n";
    outStream.write(accept.getBytes());
    String language = "Accept-Language: zh-CN\r\n";
    outStream.write(language.getBytes());
    String contenttype = "Content-Type: multipart/form-data; boundary="
        + BOUNDARY + "\r\n";
    outStream.write(contenttype.getBytes());
    String contentlength = "Content-Length: " + dataLength + "\r\n";
    outStream.write(contentlength.getBytes());
    String alive = "Connection: Keep-Alive\r\n";
    outStream.write(alive.getBytes());
    String host = "Host: " + url.getHost() + ":" + port + "\r\n";
    outStream.write(host.getBytes());
    // 写完HTTP请求头后根据HTTP协议再写一个回车换行
    outStream.write("\r\n".getBytes());
    // 把所有文本类型的实体数据发送出来
//    outStream.write(textEntity.toString().getBytes());
    // 把所有文件类型的实体数据发送出来
    for (FormFile uploadFile : files) 
    {
        StringBuilder fileEntity = new StringBuilder();
        fileEntity.append("--");
        fileEntity.append(BOUNDARY);
        fileEntity.append("\r\n");
        fileEntity.append("Content-Disposition: form-data;name=\""
            + uploadFile.getParameterName() + "\";filename=\""
            + uploadFile.getFilname() + "\"\r\n");
        fileEntity.append("Content-Type: " + uploadFile.getContentType()
            + "\r\n\r\n");
        outStream.write(fileEntity.toString().getBytes());

        // 判断是用哪儿种方法读取文件
        if (uploadFile.getInStream() != null) 
        {
        byte[] buffer = new byte[1024];
        int len = 0;
        // 一边读一边写
        while ((len = uploadFile.getInStream().read(buffer, 0, 1024)) != -1) 
        {
            outStream.write(buffer, 0, len);
        }
        uploadFile.getInStream().close();
        } else 
        {

        outStream.write(uploadFile.getData(), 0,
            uploadFile.getData().length);
        }
        outStream.write("\r\n".getBytes());
    }
    // 下面发送数据结束标志,表示数据已经结束
    outStream.write(endline.getBytes());

    BufferedReader reader = new BufferedReader(new InputStreamReader(
        socket.getInputStream()));
    // 读取web服务器返回的数据,判断请求码是否为200,也就是说判断是否成功,如果不是200,代表请求失败,那么降读取信息到流末尾
    if (reader.readLine().indexOf("200") == -1) {
        return false;
    }
    outStream.flush();
    outStream.close();
    reader.close();
    socket.close();
    return true;
    }

    // 转换ACTIVITY中的方法,把对象生成数组
    public static boolean post(String path, Map<String, String> params,
        FormFile file) throws Exception {
    return post(path, params, new FormFile[] { file });
    }
}

MainActivity中

import java.io.File;
import java.util.HashMap;
import java.util.Map;

import cn.core.utils.FormFile;
import cn.core.utils.SocketHttpRequester;
import android.app.Activity;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

public class MainActivity extends Activity {
    private EditText fileNameText;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Button button = (Button) this.findViewById(R.id.button1);
        fileNameText = (EditText) this.findViewById(R.id.filename);
        
        
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String filename = fileNameText.getText().toString();
                //如果有多个文本信息,那么可以封装到map进行操作,但是这里只有一个值封装到了MAP里
                Map<String, String> params = new HashMap<String, String>();
                params.put("filename", filename);
                try {
                    //把SDCARD卡中的文件封装成对象
                    File uploadFile = new File(Environment.getExternalStorageDirectory(), fileNameText.getText().toString());
                    //生成一个文件实体信息
                    FormFile formfile1 = new FormFile("88.png", uploadFile,"fileupload", null);
                    FormFile formfile2 = new FormFile("99.png", uploadFile,"fileupload", null);
                    //传值到工具类 ,注意看下工具类中调用的是哪儿个方法。
                    SocketHttpRequester.post("http://192.168.1.20:8080/javawebupload/servlet/ManagerServlet",
                        params, formfile1);
                    SocketHttpRequester.post("http://192.168.1.20:8080/javawebupload/servlet/ManagerServlet",
                        params, formfile2);
                    Toast.makeText(MainActivity.this, "成功", 1).show();
                } catch (Exception e) {
                    
                     Toast.makeText(MainActivity.this, "失败", 1).show();
                }
            }
        });

    }
}

记得打开以下的权限:

<!-- 在sdcard中创建与删除文件权限 -->
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
<!-- 往sdcard写入数据权限 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.INTERNET"/>

这样就可以进行文件上传操作。

posted @ 2013-03-29 13:56  小三小山  阅读(1225)  评论(0编辑  收藏  举报