因为都是测试时用的,所以功能写的很简单也不完善,仅供参考!
直接上代码:
import android.os.Handler;
import android.os.Message;
import android.util.Log;
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.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel;
import java.util.Map;
import java.util.Set;
/**
* 上传表单数据(支持多文件上传)
*
* 这里简单说明一下包含文件上传的表单提交所要遵守的规则:
* 1.请求头必须包含:Content-Type: multipart/form-data; boundary=[随便的一串字符]
* 2.请求头最好加上:Connection: Keep-Alive。因为http是无状态的协议,这里又需要传文件,加上它能使当前请求保持长连接
* 3.请求方法:POST
* 4.请求体可以包含普通表单数据,也可以是文件数据,但不管是普通表单数据还是文件数据,每一个都需要使用第1点里提到的boundary作为边界。
* 下面举例说明添加每一个【普通表单请求参数】的步骤:
* A.在往请求体添加每一个数据前都必须添加:--[boundary]\r\n 其中的"--"是固有写法的前缀,[boundary]是Content-Type里指定的boundary字符串,\r\n就是回车和换行
* B.接着添加:Content-Disposition: form-data; name="[字段名]"\r\n\r\n 其中的[字段名]就是对应哪个请求参数名(注意字段名需加上双引号),\r\n\r\n就是两次回车和换行
* C.接着添加:[字段对应的数据]\r\n
*
* 下面举例说明添加每一个【文件表单请求参数】的步骤:
* A.同上面A
* B.接着添加:Content-Disposition: form-data; name="[字段名]"; filename="[文件名]"\r\n\r\n
* 其中的[字段名]就是对应哪个请求参数名(如:<input type='file' name='file'/>里name属性的值),注意需加上双引号
* filename就是文件名(注意需加上双引号)
* \r\n\r\n就是两次回车和换行
* C.接着添加:[对应文件的数据]\r\n
*
* 有多少个参数就按照以上的步骤循环来写入就可以了。
* 全部参数写完后需添加一个结束符号:--[boundary]--\r\n
* 至此一次带文件上传的表单数据提交请求完成!
*
*
*/
public class MultipartRequest extends Thread {
// 这里个boundary(边界)为了避免出现以为添加了"--",所以没有加"--"字符串!!
private final static String boundary = "WebKitFormBoundaryVFAV6iACjwkGHYYK";
private final static String LINE = "\r\n";
// 因为这个犯了一个很低级的错误,捣鼓了半天 (-__-)!!!!
private final static String PREFIX = "--";
private final static String LAST = "--";
public final static int FILE_UPLOAD_PERSENT = 0x30;
public final static int FILE_UPLOAD_RESPONSE = 0x31;
private String url;
private Map<String, String> formData;
private String[] files;
private Handler handler;
public MultipartRequest(String url, Map<String, String> formData, Handler handler, String... files) {
this.url = url;
this.formData = formData;
this.files = files;
this.handler = handler;
}
@Override
public void run() {
try {
HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
conn.setRequestMethod("POST");
// 设置不进行缓存
conn.setUseCaches(false);
conn.setDoInput(true);
conn.setDoOutput(true);
conn.setChunkedStreamingMode(128 * 1024);// 128K,这个配置可以提高性能
//下面设置http请求头
// conn.setRequestProperty("Accept", "image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/x-shockwave-flash, application/xaml+xml, application/vnd.ms-xpsdocument, application/x-ms-xbap, application/x-ms-application, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*");
conn.setRequestProperty("Accept", "ext/plain, */*");
conn.setRequestProperty("Connection", "Keep-Alive");
// conn.setRequestProperty("Charset", "UTF-8");
// conn.setRequestProperty("Content-Length", );
conn.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + boundary);
DataOutputStream dos = new DataOutputStream(conn.getOutputStream());
StringBuilder builder = new StringBuilder();
// 处理普通表单字段
if (formData != null) {
Set<String> keys = formData.keySet();
for (String k : keys) {
builder.append(PREFIX).append(boundary).append(LINE);
builder.append("Content-Disposition: form-data; name=\"").append(k).append("\"").append(LINE).append(LINE);
builder.append(URLEncoder.encode(formData.get(k), "UTF-8")).append(LINE);
}
// 把数据写到输出流中
dos.write(builder.toString().getBytes("utf-8"));
}
// 处理文件
if (files != null) {
for (String f : files) {
File file = new File(f);
if (!file.exists()) {
System.out.println("文件不存在:" + f);
continue;
}
dos.write((PREFIX + boundary + LINE).getBytes());
String fileName = f.substring(f.lastIndexOf(File.separator) + 1);
dos.write(("Content-Disposition: form-data; name=\"file\"; filename=\"" + fileName + "\"").getBytes());
// dos.write((LINE + "Content-Type: text/plain").getBytes());
// dos.write(("Content-Type: ").append("application/octet-stream" + LINE).getBytes());
// dos.write(("Content-Transfer-Encoding: binary").getBytes());
dos.write((LINE + LINE).getBytes());
// 使用NIO的channel来处理字节流
FileInputStream fis = new FileInputStream(file);
FileChannel channel = fis.getChannel();
long current = 0;
long fileSize = channel.size();
ByteBuffer buffer = ByteBuffer.allocate(4096);
int len = channel.read(buffer);
while (len != -1) {
buffer.flip();
while (buffer.hasRemaining()) {
dos.write(buffer.get());
// 这个用来记录当前文件的上传进度
current++;
}
// 计算文件上传进度
int percent = (int) ((current * 1.0f / fileSize) * 100);
Message msg = handler.obtainMessage();
msg.arg1 = percent;
msg.obj = f;
msg.what = FILE_UPLOAD_PERSENT;
handler.sendMessage(msg);
buffer.clear();
len = channel.read(buffer);
}
dos.write(LINE.getBytes());
}
}
// 结尾
dos.write((PREFIX + boundary + LAST + LINE).getBytes());
dos.flush();
// 成功响应
if (conn.getResponseCode() == 200) {
handleResponse(conn.getInputStream());
}
} catch (IOException e) {
e.printStackTrace();
}
}
private void handleResponse(InputStream is) {
if (is == null) {
Log.e("文件上传", "InputStream is null");
return;
}
ReadableByteChannel channel = Channels.newChannel(is);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ByteBuffer buffer = ByteBuffer.allocate(4096);
try {
int len = channel.read(buffer);
while (len != -1) {
buffer.flip();
while (buffer.hasRemaining()) {
bos.write(buffer.get());
}
buffer.clear();
len = channel.read(buffer);
}
Message msg = handler.obtainMessage();
msg.what = FILE_UPLOAD_RESPONSE;
msg.obj = bos.toByteArray();
handler.sendMessage(msg);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
bos.close();
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}