Volley使用(四)
  mrMLeSzj1mp0 2023年11月02日 28 0


一.概述

今天来看看使用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�}��N�0���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Ҩ���5�8^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�]݄Bw
�5�E�y�c\�d���)N�S�뻢���[�f����r�}+/}Mmm��C�n?:��~��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">新窗口打开浏览 »</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/">«ä¸Šä¼ å¦ä¸€å¼ </a>
</p>
</div>
<div style="clear: both;"> </div>
</div>
</div>
<div id="footer">
<p>å›¾ç‰‡ä¸Šä¼ Chuantu.info创建于2010å¹´,ä¸ºç½‘å‹æä¾›å›¾ç‰‡ä¸Šä¼ å­˜å‚¨æœåŠ¡ï¼Â©2009-2011 All Rights Reserved. </p>
</div></center>
</body>
</html>


【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月08日 0

暂无评论

推荐阅读
mrMLeSzj1mp0