淘先锋技术网

首页 1 2 3 4 5 6 7

因为都是测试时用的,所以功能写的很简单也不完善,仅供参考!

直接上代码:

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();
            }
        }
    }

}