1. 程式人生 > >Http multipart/form-data多引數Post方式上傳資料

Http multipart/form-data多引數Post方式上傳資料

關於multipart/form-data格式的上傳,網上有大量的解決文章,這裡,我們使用HttpURLConnection 來完成

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的標準傳遞。