关于multipart/form-data格式的上传,网上有大量的解决文章,这里,我们使用HttpURLConnection 来完成
感谢以下文章作者提供的解决方法
http://www.cnblogs.com/zyzl/p/4526914.html
http://topmanopensource.iteye.com/blog/1605238这里,我们碰到的问题主要是如何上传一个文件,同时上传一个其他参数。当我们向后台发送数据时(只有字符串(String)参数时),服务器会收到以下这种信息
POST /test HTTP/1.1
Accept-Language: zh-CN,zh;q=0.8
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Content-Type: multipart/form-data; boundary=--HrOGHuIjDhR_gtUesEBnpWxVp9JH209p
Cache-Control: no-cache
Pragma: no-cache
Host: localhost
Connection: keep-alive
Content-Length: 224
--HrOGHuIjDhR_gtUesEBnpWxVp9JH209p
Content-Disposition: form-data; name="keyword"
test
--HrOGHuIjDhR_gtUesEBnpWxVp9JH209p
Content-Disposition: form-data; name="submit"
Convert
--HrOGHuIjDhR_gtUesEBnpWxVp9JH209p--
- 如果是向服务器发送一个文件(图片),会收到类似以下信息
POST /test HTTP/1.1
Accept: text/plain, */*
Accept-Language: zh-cn
Host: localhost
Content-Type:multipart/form-data;boundary=--testsssssss
User-Agent: WinHttpClient
Content-Length: 3693
Connection: Keep-Alive
-------------------------------7db372eb000e2
Content-Disposition: form-data; name="file"; filename="kn.jpg"
Content-Type: image/jpeg
(此处省略jpeg文件二进制数据...)
-------------------------------7db372eb000e2--
协议头看不懂没有关系,我们模仿着写就可以了(文章最下方有我们拼接的协议格式)。
从以上两条数据中,我们可以发现,数据的传输过程中,只要遵循以上的格式发送请求就可以了,资料中介绍,在上传给服务器的数据中,不是所有的协议头内容都需要说明的。
content-type是必须的,它包括一个类似标志性质的名为boundary的标志,它可以是随便输入的字符串。
以下 我们贴一段代码,来解释其中的内容
public static class FileImageUpload {
private static final int TIME_OUT = 100000; //超时时间
private static final String CHARSET = "utf-8"; //编码格式
public static final String FAILURE = "FAILURE";
/**
* android上传文件到服务器
*
* @param filePath 需要上传的文件的目录
* @param RequestURL 请求的rul
* @return 返回响应的内容
*/
public static String uploadFile(String RequestURL, String filePath, String postValue) {
//生成一个文件
File file = new File(filePath);
//边界标识 随机生成,这个作为boundary的主体内容
String BOUNDARY = UUID.randomUUID().toString();
String PREFIX = "--";
//回车换行,用于调整协议头的格式
StringLINE_END = "\r\n";
//格式的内容信息
String CONTENT_TYPE = "multipart/form-data";
try {
URL url = new URL(RequestURL);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
//设置超时时间
conn.setReadTimeout(TIME_OUT);
conn.setConnectTimeout(TIME_OUT);
//允许输入流
conn.setDoInput(true);
//允许输出流
conn.setDoOutput(true);
//不允许使用缓存
conn.setUseCaches(false);
//请求方式
conn.setRequestMethod("POST");
//设置编码 utf-8
conn.setRequestProperty("Charset", CHARSET);
//设置为长连接
conn.setRequestProperty("connection", "keep-alive");
//这里设置请求方式以及boundary的内容,即上面生成的随机字符串
conn.setRequestProperty("Content-Type", CONTENT_TYPE + ";boundary="
+ BOUNDARY);
if (file != null) {
//当文件不为空,把文件包装并且上传
//这里定义输出流,用于之后向服务器发起请求
OutputStream outputSteam = conn.getOutputStream();
DataOutputStream dos = new DataOutputStream(outputSteam);
//这里的StringBuffer 用来拼接我们的协议头
StringBuffer sb = new StringBuffer();
sb.append(PREFIX);
sb.append(BOUNDARY);
sb.append(LINE_END);
sb.append("Content-Disposition: form-data; name=\"postKey\""
+ LINE_END+LINE_END);
sb.append(postValue + LINE_END);
sb.append(PREFIX);
sb.append(BOUNDARY);
sb.append(LINE_END);
/**
* 这里重点注意:
* name里面的值为服务器端需要key 只有这个key 才可以得到对应的文件
* filename是文件的名字,包含后缀名的 比如:abc.png
*/
sb.append("Content-Disposition: form-data;
name=\"postKey\";
filename=\"" + file.getName() + "\"" + LINE_END);
//这里Content-Type 传给后台一个mime类型的编码字段,用于识别扩展名
sb.append("Content-Type: image/png; charset=" +
CHARSET + LINE_END);
sb.append(LINE_END);
dos.write(sb.toString().getBytes());
InputStream is = new FileInputStream(file);
byte[] bytes = new byte[1024];
int len = 0;
while ((len = is.read(bytes)) != -1) {
dos.write(bytes, 0, len);
}
is.close();
dos.write(LINE_END.getBytes());
byte[] end_data = (PREFIX + BOUNDARY + PREFIX + LINE_END).getBytes();
dos.write(end_data);
dos.flush();
/**
* 获取响应码 200=成功
* 当响应成功,获取响应的流
*/
int res = conn.getResponseCode();
//获取后台返回的数据
if (res == 200) {
BufferedReader bufferedReader = new BufferedReader(
new InputStreamReader(conn.getInputStream()));
String line;
StringBuffer sb_result = new StringBuffer();
while ((line = bufferedReader.readLine()) != null) {
sb_result.append(line);
}
return sb_result.toString();
}
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return FAILURE;
}
}
上面的代码中,StringBuffer拼接出来并且流输出的内容就是下面的内容
--(boundary的任意字符串)**********
Content-Disposition: form-data; name="postKey"
postValue
--(boundary的任意字符串)**********
Content-Disposition: form-data; name="postKey"; filename="file.getName()"
Content-Type: image/png; charset=utf-8
(文件的二进制数据)
--(boundary的任意字符串)**********--
代码中需要注意的几个地方,这里我们如果需要传递多个值,只需要用boundary的字符串按照上面的形式分开就可以了,然后,在传递普通字符串值的时候,name=”postKey”之后需要两次回车换行,这个问题目前不清楚是服务器导致的还是数据传输过程中会出现的问题导致的。
在传输文件数据的过程中,请求过程中postKey由服务器端指定传值内容。在下面一行字段中,Content-Type中,传递的类型,应该是用于识别扩展名的,按照mime的标准传递。