hsurich

hsurich

coder
github
telegram
x
email
steam

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

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

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源码

/**
 * 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 数组。

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 数组,然后写入输出流。

// 发送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();
加载中...
此文章数据所有权由区块链加密技术和智能合约保障仅归创作者所有。