为了验证功能,在本地程序中可简单使用URL类,代码如下:
public static String accessUrl(String urlString) throws IOException { URL url = new URL(urlString); HttpURLConnection urlcon = (HttpURLConnection) url.openConnection(); urlcon.connect(); InputStream is = urlcon.getInputStream(); BufferedReader buffer = new BufferedReader(new InputStreamReader(is)); StringBuffer bs = new StringBuffer(); String l = null; while ((l = buffer.readLine()) != null) { bs.append(l); } String result = bs.toString(); return result; }
但是放到tomcat服务器上运行时,却发现读取中文结果出现乱码。比如在调用QQ的api接口获取用户信息时,发现在java程序读取的"北京"和"男",却变成了"鍖椾含"和"鐢?。
尝试用new String(apiResult.getBytes("GBK"),"UTF-8");进行编码转发,发现双字的"北京"可以转换正确,单字的"男"或三字等字符串的最后一个字却不能正确转换。
猜测是使用URL的InputStream和Reader读取字节时使用的字节和本地环境有关:单个程序使用的是java的UTF-8,和服务器返回编码一致,不出现问题;在本地tomcat上运行时可能依赖于操作系统,使用GBK进行解码,导致出现乱码;又因为对服务器UTF8编码字节做GBK解码时导致数据丢失,再怎么编解码转不回来。
解决方案如下:
1.指定Reader的字符集
使用InputStream构建Reader时指定字符集与服务器一致,为UTF-8即可
BufferedReader buffer = new BufferedReader(new InputStreamReader(is, "UTF-8"));
2.使用HttpClient
public static String accessUrl(String urlString) throws ClientProtocolException, IOException { HttpClient httpClient = new DefaultHttpClient(); HttpGet httpGet = new HttpGet(urlString); HttpResponse response = httpClient.execute(httpGet); String message = EntityUtils.toString(response.getEntity()); // System.out.println(message); return message; }
HttpClient会优先使用响应Entity的ContentType的字符集,如果读取不到则使用指定的默认字符集,如果未指定则使用HttpClient的默认字符集(ISO-8859-1),源码如下:
public static String toString(HttpEntity entity, Charset defaultCharset) throws IOException, ParseException { ...... Charset charset = null; try { ContentType contentType = ContentType.get(entity); if(contentType != null) charset = contentType.getCharset(); } catch(UnsupportedCharsetException ex) { throw new UnsupportedEncodingException(ex.getMessage()); } if(charset == null) charset = defaultCharset; if(charset == null) charset = HTTP.DEF_CONTENT_CHARSET; Reader reader = new InputStreamReader(instream, charset); ......
所以如果服务器返回的响应里面没有ContentType且返回信息有中文,使用HttpClient也需要指定字符集
String message = EntityUtils.toString(response.getEntity(),"UTF-8");
建议使用HttpClient可以用到连接池等特性,https时也不用重复加载证书库。