2013-11-09 54 views
0

我希望在java中發佈一個包含字符串和二進制參數的表單,例如Http在Java中發佈字符串和二進制參數

名= SAM &照片= < ...二進制數據...>

不幸的是,現有的文件只包括上載字符串或二進制數據分開。我怎麼能結合這兩個?

+0

可能重複[HTTP POST在Java中(文件上傳)(http://stackoverflow.com/questions/9692166/http-post-in-java-with-file-upload) – tucuxi

+1

使用Apache Commons HTTPRequest - 手動完成是一件非常麻煩的事情。您將發送多部分請求,並且每個部分都可以擁有自己的數據。 – tucuxi

+0

apache庫如此混亂以至於建議的解決方案似乎無法正常工作。 –

回答

1

您需要發送MIME類型的「application/x-www-form-urlencoded」。這些字段必須是文本。

的字段名稱和值被轉義/編碼,例如空格字符由+', reserved characters are escaped using URL encoding. Oh and that is not all... Non-alphanumeric characters are replaced by%HH」替換爲20%空間

代表字符的ASCII碼所以兩個十六進制數字。

如果只有Java原本一些如何爲你做這個.....哦,等一下它可以...

不過是一個新的類。自從Java 1.0以來,它一直存在。

查看URLEncoder,它是HTML表單編碼的實用工具類。

該類包含將字符串轉換爲application/x-www-form-urlencoded MIME格式的靜態方法。您可以通過查閱HTML規範(下面引用)來了解關於HTML表單編碼的更多信息。

http://docs.oracle.com/javase/1.4.2/docs/api/java/net/URLEncoder.html

的URLEncoder的處理如下: 「字母數字字符 」a「 到 」z「, 」A「 到 」Z「 和 」0「 至 」9「 保持不變的特殊字符」。 。「,」 - 「,」*「和」_「保持不變。」空格字符「」被轉換爲加號「+」。 「

這裏是踢球二進制...

」所有其他字符是不安全的,並且首先被轉換成使用一些編碼方案的一個或多個字節。然後每個字節由3個字符的字符串「%xy」表示,其中xy是該字節的兩位十六進制表示。推薦使用的編碼方案是UTF-8。然而,爲了兼容性的原因,如果未指定編碼,則使用平臺的默認編碼。」

指定UTF-8總是。

這裏是HTTP規範。

http://www.w3.org/MarkUp/html-spec/html-spec_8.html

0

讓我爲你分手。(你可以在這裏抓住它BTW:http://richardhightower.github.io/site/Boon/Welcome.html

我說這福音:

public static String postForm(final String url, final Map<String, ?> headers, 
              final Map<String, Object> formData 
) 

這裏的關鍵是編碼的二進制數據:

String response = HTTP.postForm ("http://localhost:9220/test", 
      Collections.EMPTY_MAP, 
      map("hI", (Object)"hi-mom", "image", new byte[] {1,2,3}) 
    ); 

    boolean ok = true; 
    ok |= response.startsWith ("hI=hi-mom&image=%01%02%03\n") || 
      die("encoding did not work"); 

上面是測試顯示它的工作原理據我瞭解的規格。

關鍵是它將圖像,新字節[] {1,2,3}變成圖像\ u0000 =%01%02%03。

BTW地圖只是一個創建地圖(列在底部)的實用方法。

http服務器只是一個回聲。

return Exceptions.tryIt(String.class, new Exceptions.TrialWithReturn<String>() { 
     @Override 
     public String tryIt() throws Exception { 
      URLConnection connection; 
      connection = doPostFormData(url, headers, formData); 
      return extractResponseString(connection); 
     } 
    }); 

神奇發生在doPostFormData:

private static URLConnection doPostFormData(String url, Map<String, ?> headers, 
            Map<String, Object> formData 
) throws IOException { 
    HttpURLConnection connection;/* Handle output. */ 


    connection = (HttpURLConnection) new URL(url).openConnection(); 
    connection.setConnectTimeout(DEFAULT_TIMEOUT_SECONDS * 1000); 

    connection.setDoOutput(true); 

    connection.addRequestProperty ("Content-Type", "application/x-www-form-urlencoded"); 

    ByteBuf buf = ByteBuf.create (244); 



    final Set<String> keys = formData.keySet(); 

    int index = 0; 
    for (String key : keys) { 

     Object value = formData.get (key); 

     if (index > 0) { 
      buf.addByte ('&'); 
     } 


     buf.addUrlEncoded ( key ); 
     buf.addByte ('='); 

     if (! (value instanceof byte[])) { 
      buf.addUrlEncoded (value.toString()); 
     } else { 
      buf.addUrlEncodedByteArray((byte[]) value); 
     } 
     index++; 
    } 


    manageContentTypeHeaders ("application/x-www-form-urlencoded", 
      StandardCharsets.UTF_8.name(), connection); 

    manageHeaders(headers, connection); 


    int len = buf.len(); 
    IO.write(connection.getOutputStream(), 
      new String(buf.readForRecycle(), 0, len, StandardCharsets.UTF_8), IO.DEFAULT_CHARSET); 
    return connection; 
} 

通知調用addUrlEncodedByteArray你傳遞一個字節數組。 Java可以正常使用字符串的URL編碼。我找不到一個簡單的方法 來編碼一個字節數組,所以我只寫了它。

public void addUrlEncodedByteArray (byte[] value) { 



    final byte[] encoded = new byte [2]; 

    for (int index = 0; index < value.length; index++) { 
     int i = value[index]; 

     if (i >= 'a' && i <= 'z') { 
      this.addByte (i); 
     } else if (i >= 'A' && i <= 'Z') { 
      this.addByte (i); 
     } else if (i >= '0' && i <= '9') { 
      this.addByte (i); 
     } else if (i == '_' || i == '-' || i == '.' || i == '*') { 
      this.addByte (i); 
     } else if (i == ' ') { 
      this.addByte ('+'); 
     } else { 
      encodeByteIntoTwoAsciiCharBytes(i, encoded); 
      this.addByte ('%'); 
      this.addByte (encoded [0]); 
      this.addByte (encoded [1]); 
     } 

    } 
} 

它不是最漂亮的。但單元測試工作。 我相信你會得到主意。它遵循規範並相應地轉換。

不在一定範圍內的所有數據都使用%hexdigit hexdigit進行編碼。

然後你只需要這兩種方法完成了編碼:

/** 
* Turns a single nibble into an ascii HEX digit. 
* 
* @param nibble the nibble to encode. 
* 
* @return the encoded nibble (1/2 byte). 
*/ 
protected static int encodeNibbleToHexAsciiCharByte(final int nibble) { 

    switch (nibble) { 
     case 0x00: 
     case 0x01: 
     case 0x02: 
     case 0x03: 
     case 0x04: 
     case 0x05: 
     case 0x06: 
     case 0x07: 
     case 0x08: 
     case 0x09: 
      return nibble + 0x30; // 0x30('0') - 0x39('9') 
     case 0x0A: 
     case 0x0B: 
     case 0x0C: 
     case 0x0D: 
     case 0x0E: 
     case 0x0F: 
      return nibble + 0x57; // 0x41('a') - 0x46('f') 
     default: 
      die("illegal nibble: " + nibble); 
      return -1; 
    } 
} 


/** 
* Turn a single bytes into two hex character representation. 
* 
* @param decoded the byte to encode. 
* @param encoded the array to which each encoded nibbles are now ascii hex representations. 
*/ 
public static void encodeByteIntoTwoAsciiCharBytes(final int decoded, final byte[] encoded) { 

    Objects.requireNonNull (encoded); 

    boolean ok = true; 


    ok |= encoded.length == 2 || die("encoded array must be 2"); 


    encoded[0] = (byte) encodeNibbleToHexAsciiCharByte((decoded >> 4) & 0x0F); 
    encoded[1] = (byte) encodeNibbleToHexAsciiCharByte(decoded & 0x0F); 
} 

這是最重要的位。剩下的就是處理HTTP請求/頭文件gak。

這裏是manageContentTypeHeaders

manageContentTypeHeaders ("application/x-www-form-urlencoded", 
      StandardCharsets.UTF_8.name(), connection); 

... 

private static void manageContentTypeHeaders(String contentType, String charset, URLConnection connection) { 
    connection.setRequestProperty("Accept-Charset", charset == null ? StandardCharsets.UTF_8.displayName() : charset); 
    if (contentType!=null && !contentType.isEmpty()) { 
     connection.setRequestProperty("Content-Type", contentType); 
    } 
} 

下面是管理標題

manageHeaders(headers, connection); 

... 

private static void manageHeaders(Map<String, ?> headers, URLConnection connection) { 
    if (headers != null) { 
     for (Map.Entry<String, ?> entry : headers.entrySet()) { 
      connection.setRequestProperty(entry.getKey(), entry.getValue().toString()); 
     } 
    } 
} 

然後我們編碼流與UTF_8派:

int len = buf.len(); 
    IO.write(connection.getOutputStream(), 
      new String(buf.readForRecycle(), 0, len, StandardCharsets.UTF_8), IO.DEFAULT_CHARSET); 

的IO寫只是做這個: IO.write ...

public static void write (OutputStream out, String content, Charset charset) { 

    try (OutputStream o = out) { 
     o.write (content.getBytes (charset)); 
    } catch (Exception ex) { 
     Exceptions.handle (ex); 
    } 

} 

ByteBuf就像一個ByteBuffer,但更易於使用且速度非常快。我有基準。 :)

我錯過了什麼?

讓我知道它是否適合你。

--Rick

地圖功能只是實用方法,所以我可以簡明地表示地圖,因爲我覺得我用他們很多。它只會達到9或10。除此之外,我有一種方式來傳遞條目列表。

public static <K, V> Map<K, V> map(K k0, V v0) { 
    Map<K, V> map = new LinkedHashMap<>(10); 
    map.put(k0, v0); 
    return map; 
} 

public static <K, V> Map<K, V> map(K k0, V v0, K k1, V v1) { 
    Map<K, V> map = new LinkedHashMap<>(10); 
    map.put(k0, v0); 
    map.put(k1, v1); 
    return map; 
} 


public static <K, V> Map<K, V> map(K k0, V v0, K k1, V v1, K k2, V v2) { 
    Map<K, V> map = new LinkedHashMap<>(10); 
    map.put(k0, v0); 
    map.put(k1, v1); 
    map.put(k2, v2); 
    return map; 
} 

public static <K, V> Map<K, V> map(K k0, V v0, K k1, V v1, K k2, V v2, K k3, 
            V v3) { 
    Map<K, V> map = new LinkedHashMap<>(10); 
    map.put(k0, v0); 
    map.put(k1, v1); 
    map.put(k2, v2); 
    map.put(k3, v3); 
    return map; 
} 

public static <K, V> Map<K, V> map(K k0, V v0, K k1, V v1, K k2, V v2, K k3, 
            V v3, K k4, V v4) { 
    Map<K, V> map = new LinkedHashMap<>(10); 
    map.put(k0, v0); 
    map.put(k1, v1); 
    map.put(k2, v2); 
    map.put(k3, v3); 
    map.put(k4, v4); 
    return map; 
} 

public static <K, V> Map<K, V> map(K k0, V v0, K k1, V v1, K k2, V v2, K k3, 
            V v3, K k4, V v4, K k5, V v5) { 
    Map<K, V> map = new LinkedHashMap<>(10); 
    map.put(k0, v0); 
    map.put(k1, v1); 
    map.put(k2, v2); 
    map.put(k3, v3); 
    map.put(k4, v4); 
    map.put(k5, v5); 
    return map; 
} 

public static <K, V> Map<K, V> map(K k0, V v0, K k1, V v1, K k2, V v2, K k3, 
            V v3, K k4, V v4, K k5, V v5, K k6, V v6) { 
    Map<K, V> map = new LinkedHashMap<>(10); 
    map.put(k0, v0); 
    map.put(k1, v1); 
    map.put(k2, v2); 
    map.put(k3, v3); 
    map.put(k4, v4); 
    map.put(k5, v5); 
    map.put(k6, v6); 
    return map; 
} 

public static <K, V> Map<K, V> map(K k0, V v0, K k1, V v1, K k2, V v2, K k3, 
            V v3, K k4, V v4, K k5, V v5, K k6, V v6, K k7, V v7) { 
    Map<K, V> map = new LinkedHashMap<>(10); 
    map.put(k0, v0); 
    map.put(k1, v1); 
    map.put(k2, v2); 
    map.put(k3, v3); 
    map.put(k4, v4); 
    map.put(k5, v5); 
    map.put(k6, v6); 
    map.put(k7, v7); 
    return map; 
} 

public static <K, V> Map<K, V> map(K k0, V v0, K k1, V v1, K k2, V v2, K k3, 
            V v3, K k4, V v4, K k5, V v5, K k6, V v6, K k7, V v7, K k8, V v8) { 
    Map<K, V> map = new LinkedHashMap<>(10); 
    map.put(k0, v0); 
    map.put(k1, v1); 
    map.put(k2, v2); 
    map.put(k3, v3); 
    map.put(k4, v4); 
    map.put(k5, v5); 
    map.put(k6, v6); 
    map.put(k7, v7); 
    map.put(k8, v8); 
    return map; 
} 

public static <K, V> Map<K, V> map(K k0, V v0, K k1, V v1, K k2, V v2, K k3, 
            V v3, K k4, V v4, K k5, V v5, K k6, V v6, K k7, V v7, K k8, V v8, 
            K k9, V v9) { 
    Map<K, V> map = new LinkedHashMap<>(10); 
    map.put(k0, v0); 
    map.put(k1, v1); 
    map.put(k2, v2); 
    map.put(k3, v3); 
    map.put(k4, v4); 
    map.put(k5, v5); 
    map.put(k6, v6); 
    map.put(k7, v7); 
    map.put(k8, v8); 
    map.put(k9, v9); 
    return map; 
}