当字符集为 UTF-8 时使用 OutputStream 发送特殊字符时出错
Error when sending special characters using OutputStream when the charset is UTF-8
我正在使用 HttpServer 创建一个简单的服务。当我使用不含特殊字符的字符串时,该服务正常工作。
public static void main (String arg []) throws Exception {
HttpServer server = HttpServer.create(new InetSocketAddress(serverPort), 0);
server.createContext("/notification", new MyHandler());
server.setExecutor(null); // creates a default executor
server.start();
}
static class MyHandler implements HttpHandler {
public void handle(HttpExchange t) throws IOException {
String response;
response = "with special characters éáã "; // it doesn't work
response = "without special characters"; // it works!
String encoding = "UTF-8";
System.out.println(response);
t.getResponseHeaders().set("Content-Type", "application/json; charset=" + encoding);
t.sendResponseHeaders(200, response.length());
byte[] bytes = response.getBytes(StandardCharsets.UTF_8);
OutputStream os = t.getResponseBody();
os.write(bytes);
os.flush();
os.close();
}
}
当我的 UTF-8 字符串有特殊字符时,它 return 这个错误:
java.io.IOException: 写入流的字节太多
在 sun.net.httpserver.FixedLengthOutputStream.write(FixedLengthOutputStream.java:76)
在 java.io.FilterOutputStream.write(FilterOutputStream.java:97)
在 sun.net.httpserver.PlaceholderOutputStream.write(ExchangeImpl.java:439)
在内部服务器$MyHandler.handle(InternalServer.java:86)
在 com.sun.net.httpserver.Filter$Chain.doFilter(Filter.java:79)
在 sun.net.httpserver.AuthFilter.doFilter(AuthFilter.java:83)
在 com.sun.net.httpserver.Filter$Chain.doFilter(Filter.java:82)
在 sun.net.httpserver.ServerImpl$Exchange$LinkHandler.handle(ServerImpl.java:675)
在 com.sun.net.httpserver.Filter$Chain.doFilter(Filter.java:79)
在 sun.net.httpserver.ServerImpl$Exchange.run(ServerImpl.java:647)
在 sun.net.httpserver.ServerImpl$DefaultExecutor.execute(ServerImpl.java:158)
在 sun.net.httpserver.ServerImpl$Dispatcher.handle(ServerImpl.java:431)
在 sun.net.httpserver.ServerImpl$Dispatcher.run(ServerImpl.java:396)
在 java.lang.Thread.run(Thread.java:748)
问题来了。
t.sendResponseHeaders(200, response.length());
sendResponseHeaders
的第二个参数必须是您要发送的内容的确切大小字节。但是你传递的是字符串的长度 in characters.
在 UTF-8 中,任何大于 U+0080 的字符都将被编码为 2 个或更多字节。您的第二个示例字符串包含大于 U+0080 的字符,因此当它以 UTF-8 编码时,字符数和字节数不同。您将在响应中设置不正确的内容长度 header
看起来 HttpExchange
提供的输出流正在检查您发送的字节数没有超过您在响应 header 中设置的字节数。 (这将违反 HTTP 协议。)
解决方案:
...
byte[] bytes = response.getBytes(StandardCharsets.UTF_8);
t.sendResponseHeaders(200, bytes.length);
...
也可以传递0
作为内容长度。这将导致使用 chunked transfer encoding.
发送 body
我正在使用 HttpServer 创建一个简单的服务。当我使用不含特殊字符的字符串时,该服务正常工作。
public static void main (String arg []) throws Exception {
HttpServer server = HttpServer.create(new InetSocketAddress(serverPort), 0);
server.createContext("/notification", new MyHandler());
server.setExecutor(null); // creates a default executor
server.start();
}
static class MyHandler implements HttpHandler {
public void handle(HttpExchange t) throws IOException {
String response;
response = "with special characters éáã "; // it doesn't work
response = "without special characters"; // it works!
String encoding = "UTF-8";
System.out.println(response);
t.getResponseHeaders().set("Content-Type", "application/json; charset=" + encoding);
t.sendResponseHeaders(200, response.length());
byte[] bytes = response.getBytes(StandardCharsets.UTF_8);
OutputStream os = t.getResponseBody();
os.write(bytes);
os.flush();
os.close();
}
}
当我的 UTF-8 字符串有特殊字符时,它 return 这个错误:
java.io.IOException: 写入流的字节太多 在 sun.net.httpserver.FixedLengthOutputStream.write(FixedLengthOutputStream.java:76) 在 java.io.FilterOutputStream.write(FilterOutputStream.java:97) 在 sun.net.httpserver.PlaceholderOutputStream.write(ExchangeImpl.java:439) 在内部服务器$MyHandler.handle(InternalServer.java:86) 在 com.sun.net.httpserver.Filter$Chain.doFilter(Filter.java:79) 在 sun.net.httpserver.AuthFilter.doFilter(AuthFilter.java:83) 在 com.sun.net.httpserver.Filter$Chain.doFilter(Filter.java:82) 在 sun.net.httpserver.ServerImpl$Exchange$LinkHandler.handle(ServerImpl.java:675) 在 com.sun.net.httpserver.Filter$Chain.doFilter(Filter.java:79) 在 sun.net.httpserver.ServerImpl$Exchange.run(ServerImpl.java:647) 在 sun.net.httpserver.ServerImpl$DefaultExecutor.execute(ServerImpl.java:158) 在 sun.net.httpserver.ServerImpl$Dispatcher.handle(ServerImpl.java:431) 在 sun.net.httpserver.ServerImpl$Dispatcher.run(ServerImpl.java:396) 在 java.lang.Thread.run(Thread.java:748)
问题来了。
t.sendResponseHeaders(200, response.length());
sendResponseHeaders
的第二个参数必须是您要发送的内容的确切大小字节。但是你传递的是字符串的长度 in characters.
在 UTF-8 中,任何大于 U+0080 的字符都将被编码为 2 个或更多字节。您的第二个示例字符串包含大于 U+0080 的字符,因此当它以 UTF-8 编码时,字符数和字节数不同。您将在响应中设置不正确的内容长度 header
看起来 HttpExchange
提供的输出流正在检查您发送的字节数没有超过您在响应 header 中设置的字节数。 (这将违反 HTTP 协议。)
解决方案:
...
byte[] bytes = response.getBytes(StandardCharsets.UTF_8);
t.sendResponseHeaders(200, bytes.length);
...
也可以传递0
作为内容长度。这将导致使用 chunked transfer encoding.