Tomcat8/9下BitmapFactory.decodeByteArray解码图片字节数组返回null问题
看起来你遇到了一个典型的WebSocket图片传输在Tomcat独立环境下的兼容性问题,我帮你分析下根源和解决方案:
你之前用Tomcat Maven插件正常,切换到独立Tomcat(7/8/9)后BitmapFactory.decodeByteArray返回null,本质是服务器端的ByteBuffer处理逻辑有隐藏bug,Tomcat独立版本的多线程/缓冲区管理机制让这些bug暴露了出来:
1. ByteBuffer的position未重置
当你多次调用bbuf.put(msg)后,ByteBuffer的position会指向最后写入的位置,此时直接把bbuf传给sendBuf,发送的是position到limit之间的空填充数据(因为你预分配了1MB缓冲区,没填满的部分都是0)。客户端收到的字节数组包含大量无效字节,自然无法被BitmapFactory解析。
2. 消息长度统计错误
你用msg.array().length累加图片大小是错误的——ByteBuffer.array()返回的是整个底层数组(可能远大于当前消息的实际有效长度),正确的做法是用msg.remaining()获取当前消息的真实字节数。
3. 成员变量ByteBuffer的线程安全问题
bbuf作为类成员变量,多个客户端同时上传图片时会互相覆盖字节流,这在Tomcat多线程环境下必然导致数据混乱。
我重新调整了服务器端的WebSocket处理逻辑,解决了上述所有问题:
@OnMessage public void processUpload(ByteBuffer msg, boolean last, Session session) { // 每个会话单独维护上传缓冲区,避免多线程冲突 ByteArrayOutputStream baos = (ByteArrayOutputStream) session.getUserProperties().get("uploadBaos"); if (baos == null) { baos = new ByteArrayOutputStream(); session.getUserProperties().put("uploadBaos", baos); } // 提取当前消息的有效字节 byte[] chunk = new byte[msg.remaining()]; msg.get(chunk); try { baos.write(chunk); } catch (IOException e) { e.printStackTrace(); // 异常时清理资源并中断上传 session.getUserProperties().remove("uploadBaos"); return; } if (last) { byte[] imageBytes = baos.toByteArray(); System.err.println("Final Image Size : " + imageBytes.length); // 发送完整的图片字节数组 sendBuf(imageBytes); // 清理会话临时资源 try { baos.close(); } catch (IOException e) { e.printStackTrace(); } session.getUserProperties().remove("uploadBaos"); } }
- 客户端验证字节流有效性
在客户端添加日志,确认收到的字节流是否是合法的图片格式(比如JPEG的前两个字节是0xFF 0xD8):
byte [] ar = message.getImageMessage().getByteArray(); Log.d("ImageDebug", "Received bytes length: " + ar.length); // 打印图片头信息 if (ar.length >= 2) { Log.d("ImageDebug", "Image header: " + String.format("%02X %02X", ar[0], ar[1])); } Bitmap bmp = BitmapFactory.decodeByteArray(ar , 0, ar.length);
如果头不是FF D8,说明服务器端的字节流确实损坏了。
- 调整Tomcat WebSocket缓冲区配置
在server.xml的Connector中添加WebSocket缓冲区大小配置,避免大图片被截断:
<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" maxBinaryMessageBufferSize="1048576"/> <!-- 1MB,可根据你的图片大小调整 -->
- 客户端简化冗余代码
你客户端里的bmp.compress步骤是多余的,直接用原始解析的Bitmap即可,不需要重新压缩。
Tomcat Maven插件的默认配置更宽松,且通常是单线程调试模式,刚好掩盖了ByteBuffer的position问题和线程安全问题。而独立Tomcat的多线程生产环境让这些隐藏bug直接显现了出来。
内容的提问来源于stack exchange,提问作者Макс Туровец

