Volley使用(四)

一.概述

今天来看看使用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步,在最后的一个文件的末尾加上统一的结束行。

服务器地址

public final String UploadHost = "http://chuantu.biz/upload.php" ;//上传的服务器地址

文件实体类

public class FormImage {
    //参数的名称
    private String mName ;
    //文件名
    private String mFileName ;
    //文件的 mime,需要根据文档查询
    private String mMime ;
    //图片路径
    private String path ;

    public FormImage(String path) {
        this.path = path;
    }

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

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

对文件数据的封装

public class PostUploadRequest extends Request {
    private ResponseListener mListener;
    private List<FormImage> mList;
    private String BOUNDARY = "-------------abcd";
    private String MULTIPART_FORM_DATA = "multipart/form-data";
    public PostUploadRequest(String url, List<FormImage> list, ResponseListener listener) {
        super(Method.POST, url, listener);
        this.mListener = listener;
        this.mList = list;
        setRetryPolicy(new DefaultRetryPolicy(5000,DefaultRetryPolicy.DEFAULT_MAX_RETRIES,DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
    }

    @Override
    protected Response parseNetworkResponse(NetworkResponse response) {
        try {
            String jsonString = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
            return Response.success(jsonString,HttpHeaderParser.parseCacheHeaders(response));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            return Response.error(new ParseError(e));
        }
    }

    @Override
    protected void deliverResponse(Object response) {
        mListener.onResponse(response);
    }

    public byte[] getBody() throws AuthFailureError {
        if (mList == null || mList.size() == 0) {
            return super.getBody();
        }
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        int N = mList.size();
        FormImage formImage;
        for (int i = 0; i < N; i++) {
            formImage = mList.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;
    }
}

我们看看拼接成功的请求体内容:

 ������JFIF����������������C��
                                        


                                        %# , #&')*)-0-(0%()(����C
                                        
                                        

                                        (((((((((((((((((((((((((((((((((((((((((((((((((((��������"�������������������������� 
                                        �����������}��!1AQa"q2���#B��R��$3br�   
                                        %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz�������������������������������������������������������������������������������������������   
                                        ���������w��!1AQaq"2�B����   #3R�br�
                                        $4�%�&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz��������������������������������������������������������������������������������?���"ǟJ�"�w�NF]���[�cbS�P�\��w��]beP���\�V`�+�$�52�*�9�pRɫZ|~e���pWa$l�Є�U}*˝�O����ŒS.FF=��dbgy^ižI�}��N0���g�v���4#�rk� [y6�1�+�����Z��EZ<��%Œj��C91A�1�9��s�4�JL57��"�a�8�9��H2qȦ�f(�S9�N�ژOl`Ӿ�����`�T�;�%��64��s�Uy?�X~����s�N;���>�J�5x�E\l��g��X-c��l��^��~)��%➠�^�sl�Ҽ���p��M��^G�H���!��y7c%@�^ŀ�w9����ս;�5�a�+�y�Y�Q\��   ���T�ۥX�LdwD6Ӑ{�ls��c�z
                                        v29�4秭h�A%b)�?wҨ���58^q��%X�;�
                                        @��1ϭÓL���XؓQd��)�2i�1_R��MI�ޡ��8��nٌ�%�#�^����Gz�|A�o+�Ȫ���<��˹���1^�� ���^���䎹W�jH@l�5��J�?;��N��!�s�V�F%'Ԝ�"~l����{��\b�]݄Bw5E�y�c\�d���)NS�뻢���[�f����r�}+/}Mmm��Cn?:��~��5����O��

文件上传接口

public class TestApi {
    public static void uploadImg(List<String> paths,ResponseListener listener){
        List<FormImage> list = new ArrayList<>();
        for(String path:paths){
            list.add(new FormImage(path));
        }
        Request request = new PostUploadRequest(Constant.UploadHost, list, listener);
        VolleyUtil.getRequestQueue().add(request);
    }
}

图片上传验证

public class MainActivity extends AppCompatActivity {

    private Bitmap bitmap;
    private ProgressDialog dialogolg;
    private ImageView imageView1;
    private ImageView imageView2;
    public static final int ACTION_CHOOSE1 = 1;
    public static final int ACTION_CHOOSE2 = 2;
    //当前图片地址
    private String path;
    //存储图片地址的集合
    private List<String> pathList = new ArrayList<>();
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //初始化请求队列
        VolleyUtil.initialize(this);
        imageView1 = (ImageView) findViewById(R.id.imageview1);
        imageView2 = (ImageView) findViewById(R.id.imageview2);
        dialogolg = new ProgressDialog(this);
        dialogolg.setCanceledOnTouchOutside(false);
    }
    public void choose(View view){
        switch (view.getId()){
            case R.id.button1://选择第一张图片
                Intent intent = new Intent(Intent.ACTION_PICK);
                intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,"image/*");
                startActivityForResult(intent,ACTION_CHOOSE1);
                break;
            case R.id.button2://选择第二张图片
                Intent intent2 = new Intent(Intent.ACTION_PICK);
                intent2.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,"image/*");
                startActivityForResult(intent2,ACTION_CHOOSE2);
                break;
        }
    }
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        switch (requestCode){
            case ACTION_CHOOSE1:
                try {
                    if (data != null) {
                        bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(data.getData()));
                        bitmap = setScaleBitmap(bitmap, 2);
                        //根据url获取图片地址
                        path = Util.getImageAbsolutePath(this,data.getData());
                        //将地址添加到集合中
                        pathList.add(path);
                        imageView1.setImageBitmap(bitmap);
                    }
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                }
                break;
            case ACTION_CHOOSE2:
                try {
                    if (data != null) {
                        bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(data.getData()));
                        bitmap = setScaleBitmap(bitmap, 2);
                        path = Util.getImageAbsolutePath(this,data.getData());
                        pathList.add(path);
                        imageView2.setImageBitmap(bitmap);
                    }
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                }
                break;
        }
    }
    private Bitmap setScaleBitmap(Bitmap photo,int SCALE) {
        if (photo != null) {
            //为防止原始图片过大导致内存溢出,这里先缩小原图显示,然后释放原始Bitmap占用的内存
            //这里缩小了1/2,但图片过大时仍然会出现加载不了,但系统中一个BITMAP最大是在10M左右,我们可以根据BITMAP的大小
            //根据当前的比例缩小,即如果当前是15M,那如果定缩小后是6M,那么SCALE= 15/6
            Bitmap smallBitmap = zoomBitmap(photo, photo.getWidth() / SCALE, photo.getHeight() / SCALE);
            //释放原始图片占用的内存,防止out of memory异常发生
            photo.recycle();
            return smallBitmap;
        }
        return null;
    }
    public Bitmap zoomBitmap(Bitmap bitmap, int width, int height) {
        int w = bitmap.getWidth();
        int h = bitmap.getHeight();
        Matrix matrix = new Matrix();
        float scaleWidth = ((float) width / w);
        float scaleHeight = ((float) height / h);
        matrix.postScale(scaleWidth, scaleHeight);// 利用矩阵进行缩放不会造成内存溢出
        Bitmap newbmp = Bitmap.createBitmap(bitmap, 0, 0, w, h, matrix, true);
        return newbmp;
    }
    public void upload(View view){
        dialogolg.setMessage("图片上传中");
        dialogolg.show();
        TestApi.uploadImg(pathList, new ResponseListener<String>() {
            @Override
            public void onErrorResponse(VolleyError error) {
                Toast.makeText(MainActivity.this, "上传失败", Toast.LENGTH_SHORT).show();
                dialogolg.dismiss();
            }
            @Override
            public void onResponse(String response) {
                File file = new File(Environment.getExternalStorageDirectory(),"response.txt");
                try {
                    FileOutputStream fos = new FileOutputStream(file);
                    fos.write(response.getBytes());
                } catch (Exception e) {
                    e.printStackTrace();
                }
                dialogolg.dismiss();
                Toast.makeText(MainActivity.this, "上传成功", Toast.LENGTH_SHORT).show();
            }
        });
    }
}

最后我们看看上传成功后服务器返回来的结果:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title>图片上传 - 免费图片上传</title>
<meta name="图片上传" content=""/>
<meta name="免费上传图片" content=""/>
<link href="default.css" rel="stylesheet" type="text/css" media="screen"/>
<style>#conash3D0{display:none}</style>
</head>
<body><center>
<div id="logo">
<h1><a href="http://chuantu.biz/">图片上传</a></h1>
<p>免费图片上传</p>
</div>
<div id="menu">
<ul>
<li class="current_page_item"><a href="http://chuantu.biz/#upload">上传你的图片</a></li>
<li><a href="http://chuantu.biz/#histroy">上传历史</a></li>
<li><a href="http://chuantu.biz/#terms">使用条款</a></li>
</ul>
</div>
<div id="page">
<div id="page-bg">
<div id="latest-post">
<p>
<strong></strong>
你能使用这张图片的地址 URL: (<a href="http://chuantu.biz/t2/33/1458611497x3738746595.jpg" target="_blank">新窗口打开浏览 &raquo;</a>)<br/><br/>
http://chuantu.biz/t2/33/<br/>
地址 :<input value="http://chuantu.biz/t2/33/1458611497x3738746595.jpg" style="width:500px;" onclick="this.select();"/><br/>
论坛代码: <input value="[img]http://chuantu.biz/t2/33/1458611497x3738746595.jpg[/img]" style="width:500px;" onclick="this.select();"/><br/>
HTML代码: <input value="<img src=http://chuantu.biz/t2/33/1458611497x3738746595.jpg />" style="width:500px;" onclick="this.select();"/><br/><br/>
<script type="text/javascript">
    /*360*300 创建于 2016-02-26*/
    var cpro_id = "u2533339";
</script>
<script src="http://cpro.baidustatic.com/cpro/ui/c.js" type="text/javascript"></script>
<br>
<a href="http://chuantu.biz/">&laquo;上传另一张</a>
</p>
</div>
<div style="clear: both;">&nbsp;</div>
</div>
</div>
<div id="footer">
<p>图片上传 Chuantu.info创建于2010年,为网友提供图片上传存储服务!©2009-2011 All Rights Reserved. </p>
</div></center>
</body>
</html>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值