JDK中DataOutputStream.writeBytes方法传递中文时乱码

java 项目中有段封装的发送 post 请求方法

Copy
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Map;
import java.util.Map.Entry;

public class FormDataPostRequest {

public static String sendPostFormData(String url, Map<String, String> headerParams, String text) {
String result = ""; // 返回的结果
BufferedReader in = null; // 读取响应输入流

try {
//创建连接
URL apiUrl = new URL(url);
HttpURLConnection connection = (HttpURLConnection) apiUrl.openConnection();
connection.setDoOutput(true);
connection.setDoInput(true);
connection.setRequestMethod("POST");
connection.setUseCaches(false);
connection.setInstanceFollowRedirects(true);
connection.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + "*****"); // 设置boundary
if (headerParams != null) {
for (Entry<String, String> entry : headerParams.entrySet()) {
connection.setRequestProperty(entry.getKey(), entry.getValue());
}
}
connection.connect();

// 发送POST请求，包含字符串参数
DataOutputStream out = new DataOutputStream(connection.getOutputStream());
// 添加字符串参数
out.writeBytes("--*****\r\n");
out.writeBytes("Content-Disposition: form-data; name=\"text\"\r\n\r\n");
out.writeBytes(text + "\r\n");
out.writeBytes("--*****--\r\n");
out.flush();
out.close();

// 读取响应
// 定义BufferedReader输入流来读取URL的响应,并设置编码方式
in = new BufferedReader(new InputStreamReader(connection.getInputStream(), "UTF-8"));
String line;
// 读取返回的内容
while ((line = in.readLine()) != null) {
result += line;
}
} catch (Exception e) {
e.printStackTrace();
System.out.println("Http请求方法内部问题");
} finally {
try {
if (in != null) {
in.close();
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
return result;
}
}

原本使用没有什么问题，后续在使用过程中，发现当参数的值为中文时，接收方会收到乱码，查看代码发现问题出现这一行。

out.writeBytes(text + "\r\n");

查看writeBytes源码

Copy
/**
* Writes out the string to the underlying output stream as a
* sequence of bytes. Each character in the string is written out, in
* sequence, by discarding its high eight bits. If no exception is
* thrown, the counter {@code written} is incremented by the
* length of {@code s}.
*
* @param s a string of bytes to be written.
* @throws IOException if an I/O error occurs.
* @see java.io.FilterOutputStream#out
*/
public final void writeBytes(String s) throws IOException {
int len = s.length();
for (int i = 0 ; i < len ; i++) {
out.write((byte)s.charAt(i));
}
incCount(len);
}

发现字符串 s 的每个字符都会被强制转换成 byte 类型，byte 类型是 8 位的，而中文的 char 是 16 位，所以当字符为中文时，会被截断，导致乱码，可以加一些日志，看一下转换后的 byte 数组。

Copy
public final void writeBytes(String s) throws IOException {
int len = s.length();
for (int i = 0 ; i < len ; i++) {
char c = s.charAt(i);
byte b = (byte) c;
System.out.println("字符：" + c + " 转换后的byte数组：" + b);
out.write(b);
}
incCount(len);
}

最终能看到中文字符被强转后，会丢失 8 位，导致乱码。因此解决方案将字符串转换成 byte 数组，然后写入输出流。

Copy
// 发送POST请求，包含字符串参数
DataOutputStream out = new DataOutputStream(connection.getOutputStream());
// 添加字符串参数
out.writeBytes("--*****\r\n");
out.writeBytes("Content-Disposition: form-data; name=\"text\"\r\n\r\n");
out.write(text.getBytes("UTF-8"));
out.writeBytes("\r\n");
out.writeBytes("--*****--\r\n");
out.flush();
out.close();