java 内置 `httpserver` 默认以文本方式处理所有响应,直接用 `string.getbytes()` 发送二进制图片会导致字节损坏;必须对图像文件使用 `imageio` 进行解码与编码,并通过 `outputstream` 原始写入,才能确保浏览器正确渲染。
在您提供的 Java HTTP 服务器代码中,sendFile() 方法统一将资源读取为 String 并调用 getBytes() 发送,这种方式仅适用于纯文本资源(如 HTML、CSS、JS)。而 PNG、JPEG 等图像属于二进制数据,其字节流包含非 UTF-8 可映射的原始字节(如 0xFF、0xD8 等 JPEG 标志头),一旦被强制转为字符串再转回字节数组,就会发生不可逆的编码失真——这正是图片“网络请求成功但无法显示”“本地 wget 下载后打不开”的根本原因。
✅ 正确做法是:对图像资源绕过字符串转换,直接以二进制流方式读取并写入响应体。以下是优化后的 sendFile() 实现(已修复关键问题):
public static void sendFile(String _name, HttpExchange _exchange) throws IOException {
InputStream $stream = WebServer.class.getResourceAsStream(_name);
if ($stream == null) {
_exchange.sendResponseHeaders(404, -1); // -1 表示不设置 Content-Length
_exchange.close();
return;
}
// 判断是否为常见图像资源(区分大小写,推荐统一转小写处理)
String lowerName = _name.toLowerCase();
boolean isImage = lowerName.endsWith(".png") ||
lowerName.endsWith(".jpg") ||
lowerName.endsWith(".jpeg");
if (isImage) {
try {
// 1. 使用 ImageIO 读取 BufferedImage(验证格式有效性 + 解码)
BufferedImage $image = ImageIO.read($stream);
if ($image == null) {
throw new IOException("Unsupported or corrupted image format: " + _name);
}
// 2. 设置正确的 MIME 类型和响应头
String mimeType = lowerName.endsWith(".png") ? "image/png" : "image/jpeg";
_exchange.getResponseHeaders().set("Content-Type", mimeType);
// 3. 写入图像数据(ImageIO.write 自动处理二进制编码)
_exchange.sendResponseHeaders(200, 0); // 0 表示不预设长度(避免计算错误)
try (OutputStream os = _exchange.getResponseBody()) {
String formatName = lowerName.endsWith(".png") ? "png" : "jpeg";
ImageIO.write($image, formatName, os);
}
} finally {
$stream.close();
}
} else {
// 文本资源:安全读取为字节(避免 Scanner 的编码陷阱)
byte[] content = $stream.readAllBytes();
$stream.close();
String contentType = "text/html";
if (_name.endsWith(".css")) contentType = "text/css";
else if (_name.endsWith(".js")) contentType = "application/javascript";
_exchange.getResponseHeaders().set("Content-Type", contentType + "; charset=utf-8");
_exchange.sendResponseHeaders(200, content.length);
try (OutputStream os = _exchange.getResponseBody()) {
os.write(content);
}
}
_exchange.close();
}
? 关键改进说明: