Android Volley文件上传(一)

对于Volley的使用这里不做介绍,不清楚的自己找资料了解,我给大家介绍的是使用Volley网络框架来实现上传文件功能,因为volley没有自带上传文件功能,因此只能自己实现。而实现Volley文件上传前,必须了解表单的提交。

(一)表单提交

要了解表单提交必须知道表单提交的数据格式是什么样,从网站抓取一条数据,分析其表单的数据格式

Connection: keep-alive
Content-Length: 123
X-Requested-With: ShockwaveFlash/16.0.0.296
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.93 Safari/537.36
Content-Type: multipart/form-data; boundary=----------Ij5ei4KM7KM7ae0KM7cH2ae0Ij5Ef1
Accept: */*
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.8
Cookie: bdshare_firstime=1409052493497

------------Ij5ei4KM7KM7ae0KM7cH2ae0Ij5Ef1
Content-Disposition: form-data; name="position"

1425264476444
------------Ij5ei4KM7KM7ae0KM7cH2ae0Ij5Ef1
Content-Disposition: form-data; name="editorIndex"

ue_con_1425264252856
------------Ij5ei4KM7KM7ae0KM7cH2ae0Ij5Ef1
Content-Disposition: form-data; name="cm"

100672
------------Ij5ei4KM7KM7ae0KM7cH2ae0Ij5Ef1--

看上面数据格式,只需要分析数据如何封装的,因为http请求头已经帮我们封装好了。上面有三条数据,因为都一样,所以只需要分析一条,拿最后一条来分析,因为最后一条保护了结尾符。

------------Ij5ei4KM7KM7ae0KM7cH2ae0Ij5Ef1
Content-Disposition: form-data; name="cm"

100672
------------Ij5ei4KM7KM7ae0KM7cH2ae0Ij5Ef1--

第一行
其格式是 “–” + boundary + “\r\n”。”-“是数据开始标志,boundary是为http实体头定义的数据分割线,boundary可以为任意字符,只需要和————Ij5ei4KM7KM7ae0KM7cH2ae0Ij5Ef1中保持一致即可,
“\r\n”是回车换行

------------Ij5ei4KM7KM7ae0KM7cH2ae0Ij5Ef1

第二行
其格式是 “Content-Disposition: form-data; name=” + 参数名称 + “\r\n”。Content-Disposition是上传的内容特性, form-data是以表单形式上传

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

第三行
其格式是”\r\n”,回车换行

第四行
其格式是 value + “\r\n”。 value是参数的值,参数是由key和value组成

100672

第五行
其格式是“–”+boundary + “–” + “\r\n”

------------Ij5ei4KM7KM7ae0KM7cH2ae0Ij5Ef1--

说明:在所有的数据结束之后,需要有这个结尾标志。
如果有多个参数,则重复1、2、3、4,直至最后一个参数的最后加上结尾行。

一、创建表单参数实体类
这里对参数做一个封装,因为往往提交表单的时候,都需要提交多个参数

/**
 * 表单实体类
 * Created by Hlh on 2016/4/14.
 */
public class TextParams {
    private String name;
    private String value;

    public String getName() {
        return name;
    }

    public String getValue() {
        return value;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setValue(String value) {
        this.value = value;
    }
}

二、Volley 对数据的封装

/**
 * Created by Hlh on 2016/4/14.
 */
public class PostFormRequest extends Request {

    private ResponseListener mListener;
    //用来解析 json 用的
    private Gson mGson;
    //在用 gson 解析 json 数据的时候,需要用到这个参数
    private Type mClazz;
    //请求 数据通过参数的形式传入
    private List<TextParams> mListItem;
    //数据分割线
    private String BOUNDARY = "---------Hlh";
    private String MULTIPART_FORM_DATA = "multipart/form-data";

    public PostFormRequest(String url, List<TextParams> listItem, Type type, ResponseListener listener) {
        super(Method.POST, url, listener);
        this.mListener = listener;
        mGson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
        mClazz = type;
        setShouldCache(false);
        mListItem = listItem;
    }

    /**
     * 这里开始解析数据
     *
     * @param response Response from the network
     * @return
     */
    @Override
    protected Response<T> parseNetworkResponse(NetworkResponse response) {
        try {
            T result;
            String jsonString =
                    new String(response.data, HttpHeaderParser.parseCharset(response.headers));
            result = mGson.fromJson(jsonString, mClazz);
            return Response.success(result,
                    HttpHeaderParser.parseCacheHeaders(response));
        } catch (UnsupportedEncodingException e) {
            return Response.error(new ParseError(e));
        }
    }

    /**
     * 回调正确的数据
     *
     * @param response The parsed response returned by
     */
    @Override
    protected void deliverResponse(T response) {
        mListener.onResponse(response);
    }

    @Override
    public byte[] getBody() throws AuthFailureError {
        if (mListItem == null || mListItem.size() == 0) {
            return super.getBody();
        }
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        int N = mListItem.size();
        TextParams TextParams;
        for (int i = 0; i < N; i++) {
            TextParams = mListItem.get(i);
            StringBuffer sb = new StringBuffer();
            /*第一行:"--" + boundary + "\r\n" ;*/
            sb.append("--" + BOUNDARY);
            sb.append("\r\n");
            /*第二行:"Content-Disposition: form-data; name="参数的名称"" + "\r\n" ;*/
            sb.append("Content-Disposition: form-data;");
            sb.append("name=\"");
            sb.append(TextParams.getName());
            sb.append("\"");
            sb.append("\r\n");
            /*第三行:"\r\n" ;*/
            sb.append("\r\n");
            /*第四行:"参数的值" + "\r\n" ;*/
            sb.append(TextParams.getValue());
            sb.append("\r\n");
            try {
                bos.write(sb.toString().getBytes("utf-8"));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        /*结尾行:"--" + boundary + "--" + "\r\n" ;*/
        String endLine = "--" + BOUNDARY + "--" + "\r\n";
        try {
            bos.write(endLine.toString().getBytes("utf-8"));
        } catch (IOException e) {
            e.printStackTrace();
        }
        return bos.toByteArray();
    }

    /*获取内容类型,这里为表单类型*/
    @Override
    public String getBodyContentType() {
        return MULTIPART_FORM_DATA + "; boundary=" + BOUNDARY;
    }
}

1、Volley定制 Request 的时候,需要重写获取实体的方法

 public byte[] getBody() throws AuthFailureError {}

2、把参数通过二进制的形式传给服务器,当然就不需要重写获取参数的方法

protected Map<String, String> getParams() throws AuthFailureError {}

3、为了简化回调接口,这里把错误回调Response.ErrorListener 和正确的数据回调Response.Listener合并成一个ResponseListener

public interface ResponseListener<T> extends Response.ErrorListener,Response.Listener<T> {}

4、最核心的方法也就在getBody()中,这个方法的实现,如果对表单提交的数据格式很了解,实现起来非常简单,因为这个方法就是把参数拼接成我们所分析的数据格式;

 @Override
    public byte[] getBody() throws AuthFailureError {
        if (mListItem == null || mListItem.size() == 0) {
            return super.getBody();
        }
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        int N = mListItem.size();
        TextParams TextParams;
        for (int i = 0; i < N; i++) {
            TextParams = mListItem.get(i);
            StringBuffer sb = new StringBuffer();
            /*第一行:"--" + boundary + "\r\n" ;*/
            sb.append("--" + BOUNDARY);
            sb.append("\r\n");
            /*第二行:"Content-Disposition: form-data; name="参数的名称"" + "\r\n" ;*/
            sb.append("Content-Disposition: form-data;");
            sb.append("name=\"");
            sb.append(TextParams.getName());
            sb.append("\"");
            sb.append("\r\n");
            /*第三行:"\r\n" ;*/
            sb.append("\r\n");
            /*第四行:"参数的值" + "\r\n" ;*/
            sb.append(TextParams.getValue());
            sb.append("\r\n");
            try {
                bos.write(sb.toString().getBytes("utf-8"));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        /*结尾行:"--" + boundary + "--" + "\r\n" ;*/
        String endLine = "--" + BOUNDARY + "--" + "\r\n";
        try {
            bos.write(endLine.toString().getBytes("utf-8"));
        } catch (IOException e) {
            e.printStackTrace();
        }
        return bos.toByteArray();
    }

可以看到,这个方法就是实现了对数据的封装
5、 Request 中还有一个关键的地方,需要在 http 头部中声明内容类型为表单数据

Content-Type: multipart/form-data; boundary=----------Hlh

所以的重写下面方法为

public String getBodyContentType() {
        return MULTIPART_FORM_DATA+"; boundary="+BOUNDARY;
    }

通过以上内容我们不难发现,表单提交就是把文本通过二进制的形式传给服务器,从而得到对应的响应,这篇 blog 其实也算是一篇过度的文章,因为一般我们不会这么提交文字数据,那什么时候我们需要用到表单提交呢,当表单中含有附件,如图片,视频等文件的的时候;这也就是我们下一篇 blog 要讲的:Android volley 文件上传(二)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值