项目和起因

项目

  • 一个类似于Server酱的消息推送应用,只需通过一条URL即可给指定通道发送信息,可以用来发送告警、服务器状态、脚本运行状态等信息,约等于以前很多人用的邮件通知。
  • 目前只写了企业微信应用的通道,因为企业微信应用能在微信显示,而微信最常用基本不会关。
  • 以后应该会增加公众号、钉钉等通道,再看看要不要支持多人的。

遇到的问题

  • 版本:Java 1.8.0_333
  • 在Linux下使用正常,在Windows下发送中文会不显示或者乱码。

问题原因和解决

原因

  • Windows和Linux下Java默认编码不同的问题。
  • Windows下默认编码是GBK,Linux下默认编码是UTF-8。
  • 这个对新手来说挺坑的,以前一直听说Java跨平台好,没想到能遇到这种问题。
  • 在这之前用Python写过一个Dome就没遇到这种问题。
  • 可以用这段代码测试当前环境Java的默认编码
import java.io.ByteArrayOutputStream;
import java.io.OutputStreamWriter;
import java.nio.charset.Charset;
 
class Test {
    public static void main(String[] args) {
        System.out.println("Default Charset=" + Charset.defaultCharset());
        System.out.println("file.encoding=" + System.getProperty("file.encoding"));
        System.out.println("Default Charset=" + Charset.defaultCharset());
        System.out.println("Default Charset in Use=" + getDefaultCharSet());
    }
 
    private static String getDefaultCharSet() {
        OutputStreamWriter writer = new OutputStreamWriter(new ByteArrayOutputStream());
        String enc = writer.getEncoding();
        return enc;
    }
 
}

探究和解决

探索粗记录
项目基本逻辑
  1. 用SpringBoot写一个API,用来接收请求,例如:http://127.0.0.1:8080/qw?msg=你好&token=123
  2. 对比token,如果token与预设的不同返回错误信息,不给使用API。
  3. token相同,调用企业微信API把msg的信息推送到手机。
其中发送POST、GET请求的类如下:
package hello;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;

/**
 * Created by chengxia on 2018/12/4.
 */
public class HttpURLConnectionWX {
    public String doPost(String URL,String jsonStr){
        OutputStreamWriter out = null;
        BufferedReader in = null;
        StringBuilder result = new StringBuilder();
        HttpURLConnection conn = null;
        try{
            URL url = new URL(URL);
            conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("POST");
            //发送POST请求必须设置为true
            conn.setDoOutput(true);
            conn.setDoInput(true);
            //设置连接超时时间和读取超时时间
            conn.setConnectTimeout(30000);
            conn.setReadTimeout(10000);
            conn.setRequestProperty("Content-Type", "application/json");
            conn.setRequestProperty("Accept", "application/json");
            //获取输出流
            out = new OutputStreamWriter(conn.getOutputStream());
            out.write(jsonStr);
            out.flush();
            out.close();
            //取得输入流,并使用Reader读取
            if (200 == conn.getResponseCode()){
                in = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"));
                String line;
                while ((line = in.readLine()) != null){
                    result.append(line);
                    System.out.println(line);
                }
            }else{
                System.out.println("ResponseCode is an error code:" + conn.getResponseCode());
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            try{
                if(out != null){
                    out.close();
                }
                if(in != null){
                    in.close();
                }
            }catch (IOException ioe){
                ioe.printStackTrace();
            }
        }
        return result.toString();
    }

    public String doGet(String URL){
        HttpURLConnection conn = null;
        InputStream is = null;
        BufferedReader br = null;
        StringBuilder result = new StringBuilder();
        try{
            //创建远程url连接对象
            URL url = new URL(URL);
            //通过远程url连接对象打开一个连接,强转成HTTPURLConnection类
            conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("GET");
            //设置连接超时时间和读取超时时间
            conn.setConnectTimeout(15000);
            conn.setReadTimeout(60000);
            conn.setRequestProperty("Accept", "application/json");
            //发送请求
            conn.connect();
            //通过conn取得输入流,并使用Reader读取
            if (200 == conn.getResponseCode()){
                is = conn.getInputStream();
                br = new BufferedReader(new InputStreamReader(is, "UTF-8"));
                String line;
                while ((line = br.readLine()) != null){
                    result.append(line);
                    System.out.println(line);
                }
            }else{
                System.out.println("ResponseCode is an error code:" + conn.getResponseCode());
            }
        }catch (MalformedURLException e){
            e.printStackTrace();
        }catch (IOException e){
            e.printStackTrace();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            try{
                if(br != null){
                    br.close();
                }
                if(is != null){
                    is.close();
                }
            }catch (IOException ioe){
                ioe.printStackTrace();
            }
            conn.disconnect();
        }
        return result.toString();
    }


    public static void main(String[] args) throws Exception {
        // 测试用
        new HttpURLConnectionWX().doPost("http://127.0.0.1:5000/qwtx", new String("{\"name\":\"你好\"}".getBytes("UTF-8")));

    }

}
尝试
  • 使用new String(msg.getBytes("UTF-8"))进行转码,无效。
  • 使用new String(msg.getBytes("GBK"))进行转码,无效。
  • 更改POST请求函数中的in = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"));为GBK,无效。
上面的无效指的是以下效果:直接是空的(一般是纯文字信息会遇到)、前部分文字能显示最后一个是乱码(一般是文字+数字/英文)、全是乱码(瞎改代码里面的编码转换后遇到的)

更多奇怪的尝试就不说了,当时已经知道通过加参数运行可以指定编码,但是感觉那样还得按照系统改命令不够人性化,就一直在尝试。
最后还是放弃了,没找到方法,等以后真正系统学了Java再说吧。

  • 最后放个辅助测试的Python脚本
import charset_normalizer
from flask import Flask, request
import json

app = Flask(__name__)

@app.route('/')
def hello_world():
    return 'hello world'

@app.route('/qw', methods=['POST'])
def register():
    byu = request.stream.read()
    # print(request.headers)
    print(charset_normalizer.detect(byu))
    try:
        print(str(byu ,encoding='GBK'))
    except:
        print('GBK ERR')
    try:
        print(str(byu,encoding='UTF-8'))
    except:
        print('UTF-8 ERR')
    return 'welcome'

if __name__ == '__main__':
    app.run(port=5000, debug=True)
解决
参考:设置Java JDK的默认编码为UTF-8_lc11535的博客-CSDN博客_java设置utf-8
  1. 添加一个名为JAVA_TOOL_OPTIONS的系统环境变量,变量值为-Dfile.encoding=UTF-8,参考官网说明
  2. 每次运行时都加一个-Dfile.encoding=UTF-8的参数。
    如果是添加系统环境变量,添加完后需要重启CMD窗口才生效,可以用开头的检测默认编码的代码测试看看是否生效
未尝试:看B站有回复说Java 18把Win和Linux的默认编码都改成UTF-8了
最后修改:2022 年 05 月 10 日
如果觉得我的文章对你有用,请随意赞赏