怎么在android中上传文件,即怎么用Java向服务器上传文件、上传图片,这是个老问题了,在网上能搜到现成的代码,很多朋友用起来也比较熟了,但是为什么这么写,可能很多朋友并不清楚,这篇文章就来分析一下Web中文件上传的请求包内容。如有问题请联系zhfch.class@gmail.com。

  当然说,这篇文章被我冠了一个“Android”的前缀,因为我们在平常的非移动开发中,很少需要用最原始的方法来构造请求内容,但实际上,这种上传文件的方法和android是没有必然关系的,做一个C/S模式的客户端文件上传工具,也可以用这样的方法。这篇文章分为Web应用中文件上传的请求结构和用Java(在android中)上传文件/图片的方法这两部分。如果只想看怎么上传文件,推荐直接看第三部分,用开源项目提供的类库上传。

1. Web中文件上传的请求包结构分析

2. Java(Android)中的文件/图片上传代码

3. 开源库推荐

1. Web中文件上传的请求包结构分析  

  首先写了简单的Web工程,index.html中写一个表单,主要的两个表单项,一个是文本框,输入一个用户名,另一个是一个文件上传的控件,表单提交的url随便写,运行这个工程并打开这个页面:

<form action="index.jsp" method="post">
<input type="text" name="username" />
<input type="file" name="mfile" />
<input type="submit" />
</form>

  另外我写了一个简单的Web代理服务器,监听8033端口,把请求数据拦截下来并全部打印出来(这是为了看请求包的完整结构,图省事的话可以参考浏览器开发人员工具中的数据包分析),然后修改浏览器中代理服务器的设置,改成localhost的8033端口,此时在文本框中输入username为tjutester,然后选择一个桌面上的本地文件test.txt,里面只有一行字:“这里是测试文字”,点击提交,会看到代理服务器打印出的POST请求内容:

POST http://localhost:8080/ServerDemo/index.jsp HTTP/1.1
Host: localhost:8080
......(一堆其他属性,这里略过,下文还会贴上)
Cookie: JSESSIONID=4FA349F284EEE82AD5E15D571A0E9869 username=tjutester&mfile=test.txt

  这是这一次POST请求的内容,发现并没有文件的内容,当然在服务器上也是拿不到的了,因为form表单没有设置enctype属性值,浏览器将使用其默认值"application/x-www-form-urlencoded",根据结果可以看到,对于这种编码方式,所有字段按URL参数的形式排成一行,在服务端可以直接用getParameter方法来处理,这个时候mfile域跟username域在性质上没有什么区别,都是简单的文本,现在在<form>标签内添加enctype属性:

<form action="getnews" method="post" enctype="multipart/form-data">

  刷新网页后重新发送请求,可以得到下面的头部信息:

POST http://localhost:8080/ServerDemo/index.jsp HTTP/1.1
Host: localhost:8080
Proxy-Connection: keep-alive
Content-Length: 311
Cache-Control: max-age=0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Origin: http://localhost:8080
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.64 Safari/537.31
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary22uii9i7wXAwzBvK
Referer: http://localhost:8080/ServerDemo/
Accept-Encoding: gzip,deflate,sdch
Accept-Language: zh-CN,zh;q=0.8
Accept-Charset: GBK,utf-8;q=0.7,*;q=0.3
Cookie: JSESSIONID=4FA349F284EEE82AD5E15D571A0E9869 ------WebKitFormBoundary22uii9i7wXAwzBvK
Content-Disposition: form-data; name="username" tjutester
------WebKitFormBoundary22uii9i7wXAwzBvK
Content-Disposition: form-data; name="mfile"; filename="test.txt"
Content-Type: text/plain 这里是测试文字
------WebKitFormBoundary22uii9i7wXAwzBvK--

  抽取其精华,也就是如下的结构:

Content-Type: multipart/form-data; boundary=----分隔符字符串

------分隔符字符串
Content-Disposition: form-data; name="字段名" 属性值
------分隔符字符串
Content-Disposition: form-data; name="字段名"; filename="本地的文件名"
Content-Type: 文件类型 文件内容(在网络上传输就是二进制流了)
------分隔符字符串--

  服务器会在这样的请求中拿到文件内容,那么我们就在Java程序中人工构造这样的请求内容交给服务器,就完成了文件或图片的上传。那么,这个分隔符字符串和“-”有什么讲究呢,分隔符字符串是没什么讲究的,前后保持一致就可以了,Chrome生成的请求,都是WebKitFormBoundaryxxxxxxx这样的形式,用IE的话就是一个普通的数字字母串,如7dd2029110746,每个上传的参数前面有一个“--boundary”,最后是一个“--boundary--”表示终止。

2. Java(Android)中的文件/图片上传代码

  这一部分的代码具体含义我就不多做说明了,就是在拼上面的请求串,核心类的内容:

package org.fletcher.android.net;

import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.ProtocolException;
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.ClientProtocolException;
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 HttpRequester {
private static final String BOUNDARY = "-------45962402127348";
private static final String FILE_ENCTYPE = "multipart/form-data"; public static InputStream post(String urlStr, Map<String, String> params,
Map<String, File> images) {
InputStream is = null; try {
URL url = new URL(urlStr);
HttpURLConnection con = (HttpURLConnection) url.openConnection(); con.setConnectTimeout(5000);
con.setDoInput(true);
con.setDoOutput(true);
con.setUseCaches(false);
con.setRequestMethod("POST");
con.setRequestProperty("Connection", "Keep-Alive");
con.setRequestProperty("Charset", "UTF-8");
con.setRequestProperty("Content-Type", FILE_ENCTYPE + "; boundary="
+ BOUNDARY); StringBuilder sb = null;
DataOutputStream dos = new DataOutputStream(con.getOutputStream());;
if (params != null) {
sb = new StringBuilder();
for (String s : params.keySet()) {
sb.append("--");
sb.append(BOUNDARY);
sb.append("\r\n");
sb.append("Content-Disposition: form-data; name=\"");
sb.append(s);
sb.append("\"\r\n\r\n");
sb.append(params.get(s));
sb.append("\r\n");
} dos.write(sb.toString().getBytes());
} if (images != null) {
for (String s : images.keySet()) {
File f = images.get(s);
sb = new StringBuilder();
sb.append("--");
sb.append(BOUNDARY);
sb.append("\r\n");
sb.append("Content-Disposition: form-data; name=\"");
sb.append(s);
sb.append("\"; filename=\"");
sb.append(f.getName());
sb.append("\"\r\n");
sb.append("Content-Type: image/jpeg");  //这里注意!如果上传的不是图片,要在这里改文件格式,比如txt文件,这里应该是text/plain
sb.append("\r\n\r\n");
dos.write(sb.toString().getBytes()); FileInputStream fis = new FileInputStream(f);
byte[] buffer = new byte[1024];
int len;
while ((len = fis.read(buffer)) != -1) {
dos.write(buffer, 0, len);
}
dos.write("\r\n".getBytes());
fis.close();
} sb = new StringBuilder();
sb.append("--");
sb.append(BOUNDARY);
sb.append("--\r\n");
dos.write(sb.toString().getBytes());
}
dos.flush(); if (con.getResponseCode() == 200)
is = con.getInputStream(); dos.close();
// con.disconnect();
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ProtocolException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return is;
} public static byte[] read(InputStream inStream) throws Exception{
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len = 0;
while( (len = inStream.read(buffer)) != -1){
outputStream.write(buffer, 0, len);
}
inStream.close();
return outputStream.toByteArray();
}
}

  在Android界面当中,点击按钮选择图片,然后调用这个类上传,在Android中怎么选择图片,直接copy了这位仁兄的代码(http://www.oschina.net/question/157182_53236):

private static final int RESULT_LOAD_IMAGE = 0;
TextView tv; @Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); Button b = (Button) findViewById(R.id.button1);
tv = (TextView) findViewById(R.id.tv1);
b.setOnClickListener(new OnClickListener() { @Override
public void onClick(View v) {
// TODO Auto-generated method stub
Intent i = new Intent(
Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
startActivityForResult(i, RESULT_LOAD_IMAGE);
}
});
} @Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == RESULT_LOAD_IMAGE && resultCode == RESULT_OK && null != data) {
Uri selectedImage = data.getData();
String[] filePathColumn = { MediaStore.Images.Media.DATA };
Cursor cursor = getContentResolver().query(selectedImage, filePathColumn, null, null, null);
cursor.moveToFirst();
int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
final String picturePath = cursor.getString(columnIndex);
cursor.close(); new AsyncTask<Void, Void, String>() {
@Override
protected String doInBackground(Void... params) {
// TODO Auto-generated method stub
Map<String, File> maps = new HashMap<String, File>();
maps.put("image", new File(picturePath));
InputStream is = HttpRequester.post("http://192.168.199.2/Test/Upload/upload", null, maps);
try {
return new String(HttpRequester.read(is));
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return "";
}
@Override
protected void onPostExecute(String result) {
// TODO Auto-generated method stub
super.onPostExecute(result);
tv.setText(result);
}
}.execute((Void)null);
}
}

3. 推荐开源项目:android-async-http

  实际开发当中,在理解原理之后,当然还是应该站在巨人的肩膀上写程序了,我做URL请求,都是用的android-async-http这个开源库:

  http://loopj.com/android-async-http/

  用起来很简单,网站上也有一些建议的用法和API,上传文件就简单地用

RequestParams params = new RequestParams();
params.put("image", new File(path));

  来指定参数就可以了。

  

【Android】图片(文件)上传的请求分析结构的更多相关文章

  1. 使用.NET框架、Web service实现Android的文件上传(二)

    aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAAAYUAAAKpCAIAAADcx6fPAAAgAElEQVR4nOydd1hT5+LHg1attbfr1t ...

  2. .Net Core 图片文件上传下载

    当下.Net Core项目可是如雨后春笋一般发展起来,作为.Net大军中的一员,我热忱地拥抱了.Net Core并且积极使用其进行业务的开发,我们先介绍下.Net Core项目下实现文件上传下载接口. ...

  3. Android OkHttp文件上传与下载的进度监听扩展

    http://www.loongwind.com/archives/290.html 上一篇文章介绍了用Retrofit实现文件的上传与下载,但是我们发现没办法监听上传下载的进度,毕竟我们在做开发的时 ...

  4. springmvc图片文件上传接口

    springmvc图片文件上传 用MultipartFile文件方式传输 Controller package com.controller; import java.awt.image.Buffer ...

  5. SpringMvc MultipartFile 图片文件上传

    spring-servlet.xml <!-- SpringMVC上传文件时,需要配置MultipartResolver处理器 --> <bean id="multipar ...

  6. android批量文件上传(android批量图片上传)

    项目中多处用到文件批量上传功能,今天正好解决了此问题,在此写出来,以便日后借鉴. 首先,以下架构下的批量文件上传可能会失败或者不会成功:   1.android客户端+springMVC服务端:服务端 ...

  7. 文件上传漏洞靶场分析 UPLOAD_LABS

    文件上传漏洞靶场(作者前言) 文件上传漏洞 产生原理 PASS 1) function checkFile() { var file = document.getElementsByName('upl ...

  8. iOS分享 - AFNetworking之多图片/文件上传

    在分享经验之前,先说点题外话,之前的一个项目涉及到了多图片的上传,本来以为是一个很简单的事情,却着实困扰了我好久,究其原因,一是我不够细心,二是与后台人员的交流不够充分.在此,我想将我的老师常说的一句 ...

  9. SSM + Android 网络文件上传下载

    SSM + Android 网络交互的那些事 2016年12月14日 17:58:36 ssm做为后台与android交互,相信只要是了解过的人都知道一些基本的数据交互,向json,对象,map的交互 ...

随机推荐

  1. 搞定linux的中文输入和vim

    本篇是http://blog.csdn.net/guochaoxxl/article/details/53212090的姊妹篇,无论先操作哪一篇都可以: 1.一言不合先下载,链接: https://p ...

  2. Oracle SQL优化进阶学习

    引言 对于下面的Oracle分页如何优化该段语句: SELECT * FROM (SELECT A.*, ROWNUM RN FROM (SELECT * FROM task_log order by ...

  3. python3模拟扑克牌

    python3.6环境 import collections from random import choice Card=collections.namedtuple('Card',['rank', ...

  4. qemu相关命令使用

    qemu-ga qemu-guest-agent-2.5.0-3.el7.x86_64 qemu-img qemu-img-1.5.3-105.el7_2.4.x86_64 qemu-io qemu- ...

  5. Web模糊测试工具Powerfuzzer

    Web模糊测试工具Powerfuzzer   Powerfuzzer是Kali Linux自带的一款Web模糊测试工具.该工具基于各种开源模糊测试工具构建,集成了大量安全信息.该工具高度智能化,它能根 ...

  6. 原生js获取元素的样式信息

    工作中经常会需要获取DOM元素的样式,之前都是通过jquery的css()方法,现在总结一下通过原生js获取元素样式的方法. obj.style js var _width = obj.style.w ...

  7. python模块相关

    aniso8601 pyquery networkx (2.0)                 - Python package for creating and manipulating grap ...

  8. 【GLSL教程】(二)在OpenGL中使用GLSL 【转】

    http://blog.csdn.net/racehorse/article/details/6616256 设置GLSL 这一节讲述在OpenGL中配置GLSL,假设你已经写好了顶点shader和像 ...

  9. angular js 使用$location问题整理

    angular js 自带的$location方法十分强大,通过使用$location方法.我们能够获取到server的port.杂乱连接中的path()部分(/所包括的部分). 例: // give ...

  10. ElasticSearch命令增加字段总结

    1.建立一个String类型的字段 curl -XPUT http://192.168.46.163:9200/t_risk_case/_mapping/t_risk_case?pretty -d ' ...