Android volley 解析(三)之文件上传篇

前面我们讲了如何通过 volley 实现表单的提交,而这篇文章跟上一篇衔接很大,如果没有看上一篇 blog 的朋友,建议先去看看 Android Volley解析(二)之表单提交篇

因为文件上传实质就是表单的提交,只不过它提交的数据包含文件类型,接下来还是按照表单提交的套路来分析。

数据格式

这里我们通过图片上传的案例来分析,其他文件也是同样的实现方式;以下是我在传图网传图时,上传的数据格式,先来分析一下

POST http://chuantu.biz/upload.php HTTP/1.1
Host: chuantu.biz
Connection: keep-alive
Content-Length: 4459
Cache-Control: max-age=0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Origin: http://chuantu.biz
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=----WebKitFormBoundaryS4nmHw9nb2Eeusll
Referer: http://chuantu.biz/
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.8
Cookie: __cfduid=d9215d649e6e648e0eac7688b406a3d911425089350

------WebKitFormBoundaryS4nmHw9nb2Eeusll
Content-Disposition: form-data; name="uploadimg"; filename="spark_bg.png"
Content-Type: image/png

JFIFC
    %# , #&‘)*)-0-(0%()(C
    ((((((((((((((((((((((((((((((((((((((((((((((((((("
    }!1AQa"q2#BR$3br
    %&‘()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz
    w!1AQaq"2B  #3Rbr
    $4%&‘()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz?PNG
------WebKitFormBoundaryS4nmHw9nb2Eeusll--

不难发现,这种格式跟表单提交的格式非常接近,不过还是有所差别,这里仔细看还是能看出来总共有加上结尾行,有五行,因为乱码部分,其实就是图片的二进制数,整个算一行;下面来分析下:

1、第一行:"--" + boundary + "\r\n" ;

前面也说了文件上传,其实就是表单提交,所以在提交数据的开始标志不变;

2、第二行:Content-Disposition: form-data; name="参数的名称"; filename="上传的文件名" + "\r\n"

这里比普通的表单多了一个filename=”上传的文件名”;

3、第三行:Content-Type: 文件的 mime 类型 + "\r\n"

这一行是文件上传必须要的,而普通的文字提交可有可无,mime 类型需要根据文档查询;

4、第四行:"\r\n"

5、第五行文件的二进制数据 + "\r\n"

这里跟普通表单提交一样;

结尾行:"--" + boundary + "--" + "\r\n"

可以看到,文件上传的诗句格式跟我们上一篇博文中讲到的表单提交只有两个地方不同,1、第二行的时候增加了一个文件名变量,2、增加了一行Content-Type: 文件的 mime 类型 + "\r\n"

文件也可以同时上传多个文件,上传多个文件的时候重复1、2、3、4、5步,在最后的一个文件的末尾加上统一的结束行。

文件实体类

这里是对图片操作所以我建了一个FormImg.java

/**
 * Created by moon.zhong on 2015/3/3.
 */
public class FormImage {
    //参数的名称
    private String mName ;
    //文件名
    private String mFileName ;
    //文件的 mime,需要根据文档查询
    private String mMime ;
    //需要上传的图片资源,因为这里测试为了方便起见,直接把 bigmap 传进来,真正在项目中一般不会这般做,而是把图片的路径传过来,在这里对图片进行二进制转换
    private Bitmap mBitmap ;

    public FormImage(Bitmap mBitmap) {
        this.mBitmap = mBitmap;
    }

    public String getName() {
//        return mName;
//测试,把参数名称写死
        return "uploadimg" ;
    }

    public String getFileName() {
    //测试,直接写死文件的名字
        return "test.png";
    }
    //对图片进行二进制转换
    public byte[] getValue() {
        ByteArrayOutputStream bos = new ByteArrayOutputStream() ;
        mBitmap.compress(Bitmap.CompressFormat.JPEG,80,bos) ;
        return bos.toByteArray();
    }
    //因为我知道是 png 文件,所以直接根据文档查的
    public String getMime() {
        return "image/png";
    }
}

Volley 对文件数据的封装

/**
 * Created by gyzhong on 15/3/1.
 */
public class PostUploadRequest extends Request<String> {

    /**
     * 正确数据的时候回掉用
     */
    private ResponseListener mListener ;
    /*请求 数据通过参数的形式传入*/
    private List<FormImage> mListItem ;

    private String BOUNDARY = "--------------520-13-14"; //数据分隔线
    private String MULTIPART_FORM_DATA = "multipart/form-data";

    public PostUploadRequest(String url, List<FormImage> listItem, ResponseListener listener) {
        super(Method.POST, url, listener);
        this.mListener = listener ;
        setShouldCache(false);
        mListItem = listItem ;
        //设置请求的响应事件,因为文件上传需要较长的时间,所以在这里加大了,设为5秒
        setRetryPolicy(new DefaultRetryPolicy(5000,DefaultRetryPolicy.DEFAULT_MAX_RETRIES,DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
    }

    /**
     * 这里开始解析数据
     * @param response Response from the network
     * @return
     */
    @Override
    protected Response<String> parseNetworkResponse(NetworkResponse response) {
        try {
            String mString =
                    new String(response.data, HttpHeaderParser.parseCharset(response.headers));
            Log.v("zgy", "====mString===" + mString);

            return Response.success(mString,
                    HttpHeaderParser.parseCacheHeaders(response));
        } catch (UnsupportedEncodingException e) {
            return Response.error(new ParseError(e));
        }
    }

    /**
     * 回调正确的数据
     * @param response The parsed response returned by
     */
    @Override
    protected void deliverResponse(String 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() ;
        FormImage formImage ;
        for (int i = 0; i < N ;i++){
            formImage = mListItem.get(i) ;
            StringBuffer sb= new StringBuffer() ;
            /*第一行*/
            //`"--" + BOUNDARY + "\r\n"`
            sb.append("--"+BOUNDARY);
            sb.append("\r\n") ;
            /*第二行*/
            //Content-Disposition: form-data; name="参数的名称"; filename="上传的文件名" + "\r\n"
            sb.append("Content-Disposition: form-data;");
            sb.append(" name=\"");
            sb.append(formImage.getName()) ;
            sb.append("\"") ;
            sb.append("; filename=\"") ;
            sb.append(formImage.getFileName()) ;
            sb.append("\"");
            sb.append("\r\n") ;
            /*第三行*/
            //Content-Type: 文件的 mime 类型 + "\r\n"
            sb.append("Content-Type: ");
            sb.append(formImage.getMime()) ;
            sb.append("\r\n") ;
            /*第四行*/
            //"\r\n"
            sb.append("\r\n") ;
            try {
                bos.write(sb.toString().getBytes("utf-8"));
                /*第五行*/
                //文件的二进制数据 + "\r\n"
                bos.write(formImage.getValue());
                bos.write("\r\n".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();
        }
        Log.v("zgy","=====formImage====\n"+bos.toString()) ;
        return bos.toByteArray();
    }
    //Content-Type: multipart/form-data; boundary=----------8888888888888
    @Override
    public String getBodyContentType() {
        return MULTIPART_FORM_DATA+"; boundary="+BOUNDARY;
    }
}

因为代码中注解写的比较详细,加上很多东西在前面几篇 blog 已经讲过了,所以这里直接上代码。

文件上传接口

/**
 * Created by moon.zhong on 2015/3/3.
 */
public class UploadApi {

    /**
     * 上传图片接口
     * @param bitmap 需要上传的图片
     * @param listener 请求回调
     */
    public static void uploadImg(Bitmap bitmap,ResponseListener listener){
        List<FormImage> imageList = new ArrayList<FormImage>() ;
        imageList.add(new FormImage(bitmap)) ;
        Request request = new PostUploadRequest(Constant.UploadHost,imageList,listener) ;
        VolleyUtil.getRequestQueue().add(request) ;
    }
}

图片上传验证

上传类PostUploadActivity.java

/**
 * Created by moon.zhong on 2015/3/2.
 */
public class PostUploadActivity extends ActionBarActivity {
    private TextView mShowResponse ;
    private ImageView mImageView ;
    private ProgressDialog mDialog ;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_upload_img);
        mShowResponse = (TextView) findViewById(R.id.id_show_response) ;
        mImageView = (ImageView) findViewById(R.id.id_show_img) ;
        mDialog = new ProgressDialog(this) ;
        mDialog.setCanceledOnTouchOutside(false);
    }

    public void uploadImg(View view){
        mDialog.setMessage("图片上传中...");
        mDialog.show();
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.mipmap.logo) ;
        UploadApi.uploadImg(bitmap,new ResponseListener<String>() {
            @Override
            public void onErrorResponse(VolleyError error) {
                Log.v("zgy","===========VolleyError========="+error) ;
                mShowResponse.setText("ErrorResponse\n"+error.getMessage());
                Toast.makeText(PostUploadActivity.this,"上传失败",Toast.LENGTH_SHORT).show() ;
                mDialog.dismiss();
            }

            @Override
            public void onResponse(String response) {
                response = response.substring(response.indexOf("img src="));
                response = response.substring(8,response.indexOf("/>")) ;
                Log.v("zgy","===========onResponse========="+response) ;
                mShowResponse.setText("图片地址:\n"+response);
                mDialog.dismiss();
                Toast.makeText(PostUploadActivity.this,"上传成功",Toast.LENGTH_SHORT).show();
            }
        }) ;
    }
}

测试结果如下:

上传图片页面:

图片上传中

图片上传成功,地址为http://www.chuantu.biz/t/67/1425474351x-1376440163.png

通过网页请求

可以看到,volley 实现文件上传的操作还是很方便的,不过,不知道大家看到这里有没有觉得哪里有问题呢?其实 volley 实现文件上传是有一个很大的问题,什么问题呢,大家自己先想想,我将会在后续的文章中讲到这个问题,并提供解决方案(是后续,不是下一篇)。volley 讲到这里为止,对于它的功能也讲了一大部分,不过还有一个非常有用的知识点没有讲到,那就是volley缓存机制,下一节,将开启 volley 的缓存之旅,敬请期待!

点击下载源码

时间: 2024-10-25 13:10:46

Android volley 解析(三)之文件上传篇的相关文章

Android volley 解析(四)之缓存篇

这是 volley 的第四篇 blog 了,写完这篇,volley 的大部分用法也都算写了一遍,所以暂时不会写 volley 的文章了,如果想看我前面写的文章,可以点这里 Android volley 解析(三)之文件上传篇 为什么要用缓存 我们知道,当客户端在请求网络数据的时候,是需要消耗流量的,特别是对于移动端用户来说,对于流量的控制要求很高.所以在做网络请求的时候,如果对数据更新要求不是特别高,往往都会用到缓存机制,一方面能减少对服务端的请求,控制流量:另一方面,当客户端在没有网络的情况下

Android中自定义MultipartEntity实现文件上传以及使用Volley库实现文件上传

最近在参加CSDN博客之星,希望大家给投一票,谢谢啦~                       点这里投我一票吧~ 前言 在开发当中,我们常常需要实现文件上传,比较常见的就是图片上传,比如修改个头像什么的.但是这个功能在Android和iOS中都没有默认的实现类,对于Android我们可以使用Apache提供的HttpClient.jar来实现这个功能,其中依赖的类就是Apache的httpmime.jar中的MultipartEntity这个类.我就是要实现一个文件上传功能,但是我还得下载

Android Day05-网络编程之文件上传

Android文件上传实现 分析:利用抓包工具查看文件上传发现,文件上传的请求体十分的复杂,根本难以用 HttpURLConnection.HttpClient来实现.但是用AsyncHttpClient就能很轻易的实现了,只要 在RequestParams对象里面添加一对键值对,值存储文件的路径即可上传了. AsyncHttpClient文件上传源代码:

小白日记38:kali渗透测试之Web渗透-手动漏洞挖掘(三)-文件上传漏洞

手动漏洞挖掘 文件上传漏洞[经典漏洞,本身为一个功能,根源:对上传文件的过滤机制不严谨] <?php echo shell_exec($_GET['cmd']);?> 直接上传webshell 修改文件类型上传webshell 文件头,扩展名 修改扩展名上传webshell 静态解析文件扩展名时可能无法执行 文件头让偶过滤上传webshell 上传目录权限 正常上传 当可以上传一个页面文件或一句话木马等时,则可验证存在该漏洞 #低安全级别 绕过:可截包重放,修改上传文件大小等限制 #中等级别

ASIHTTPRequest系列(三):文件上传

五.文件上传 1.服务端 文件上传需要服务端的配合.我们可在本机搭建tomcat测试环境.关于tomcat在MacOSX下的安装配置,参考作者另一博文<安装Tomcat到Mac OSX>. 打开Eclipse,新建web工程.在其中新建一个ServletUploadServlet: import java.io.*; import java.util.*; importjavax.servlet.ServletException; importjavax.servlet.http.HttpSe

Android Webview实现图片、文件上传及启动相机功能

直接上代码,体会Webview强大的功能. webView.setWebChromeClient(webChromeClient); private String mCameraFilePath = null; private ValueCallback<Uri> mUploadMessage;// 表单的数据信息 private final static int FILECHOOSER_RESULTCODE = 1;// 表单的结果回调</span> <span style

Android Volley解析(二)之表单提交篇

上一篇文章中,讲了 Volley 的 get 和 post 请求,并且对 volley 的基本使用和基本分析做了讲解,而这篇 blog 将讲解用 volley 实现表单的提交,在看这篇文章之前,如果对 Volley 基本知识不够了解的朋友,可以移驾前往Android Volley解析(一)之GET.POST请求篇 表单提交的数据格式 要实现表单的提交,就要知道表单提交的数据格式是怎么样,这里我从某知名网站抓了一条数据,先来分析别人提交表单的数据格式. 数据包: Connection: keep-

java里面的文件上传与下载

文件的上传与下载主要用到两种方法:1.方法一:commons-fileupload.jar commons-io.jarapache的commons-fileupload实现文件上传,下载 [upload]package com.handson.bbs.servlet; import java.io.File;import java.io.IOException;import java.util.Date;import java.util.List;import javax.servlet.Se

文件上传的思考 (转) http://blog.csdn.net/ncafei/article/details/53401961

文件上传校验 客户端JavaScript校验(一般只校验后缀名) 一般都是在网页上写一段javascript脚本,校验上传文件的后缀名,有白名单形式也有黑名单形式. 判断方式:在浏览加载文件,但还未点击上传按钮时便弹出对话框,内容如:只允许上传.jpg/.jpeg/.png后缀名的文件,而此时并没有发送数据包. 服务端校验: 1.文件头content-type字段校验(image/gif):代码对上传文件的文件类型进行了判断,如果不是图片类型,返回错误. 2.文件内容头校验(GIF89a): 可