使用ByteBuffer创建黑色BMP图片时文件损坏问题排查
问题分析与修复方案
核心问题与修复步骤
1. 字节序不匹配(导致图片参数异常)
BMP格式要求所有多字节字段(文件大小、宽高、像素深度等)必须用小端字节序存储,但Java的ByteBuffer默认是大端字节序,这会让图片解析工具读取到错误的文件头参数,直接判定图片损坏。
修复:创建ByteBuffer时强制指定小端序:
ByteBuffer buffer = ByteBuffer.wrap(header).order(ByteOrder.LITTLE_ENDIAN);
2. 缺失保留字段写入
BMP文件头偏移4-7字节的两个保留字段(reserved1和reserved2),即使值为0也必须显式写入,原代码跳过这部分导致文件头结构不完整。
修复:在文件头写入流程中添加:
buffer.putShort(reserved1); buffer.putShort(reserved2);
3. 图像行未按4字节对齐(导致数据长度错误)
BMP规范要求每行像素数据的字节数必须是4的倍数,不足部分用0填充。原代码直接用width * height * 3计算图像大小,未考虑对齐,导致图像数据总长度不符合规范。
修复:
- 计算对齐后的单行字节数:
int rowSize = (width * 3 + 3) / 4 * 4 - 重新计算总图像大小:
imageSize = rowSize * height - 生成图像数据时按对齐后的行结构填充(补0部分数组初始化时默认是0,无需额外操作)
4. 其他细节修正
- 修正变量名拼写错误:
heigth改为height - 明确BMP像素存储顺序是BGR(蓝-绿-红),后续修改颜色时需遵循此顺序
修复后的完整代码
package co.edu.uptc.persistence; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; public class BMFile { private String fileName; private int width; private int height; private int imageSize; private int fileSize; private short reserved1; private short reserved2; private int dataBegin; private int headSize; private short layers; private short pixelSize; private int compression; private int horizontalResolution; private int verticalResolution; private int colorTableSize; private int countMainColor; public BMFile(String fileName, int width, int height) { this.fileName = fileName; this.width = width; this.height = height; // 计算每行对齐后的字节数 int rowSize = (width * 3 + 3) / 4 * 4; this.imageSize = rowSize * height; this.fileSize = imageSize + 54; // 文件头固定54字节 this.reserved1 = 0; this.reserved2 = 0; this.dataBegin = 54; this.headSize = 40; this.layers = 1; this.pixelSize = 24; this.compression = 0; this.horizontalResolution = 0; this.verticalResolution = 0; this.colorTableSize = 0; this.countMainColor = 0; } public void saveFile() throws Exception { createFile(fileName, createHeader(), createImage()); } private void createFile(String objectFile, byte[] header, byte[] image) throws Exception { try (FileOutputStream fuente = new FileOutputStream(objectFile)) { fuente.write(header); fuente.write(image); } catch (FileNotFoundException e) { throw new Exception("Ha ocurrido un error archivo no encontrado"); } catch (IOException e) { throw new Exception("Ha ocurrido un error I/O"); } } private byte[] createHeader() { byte[] header = new byte[54]; ByteBuffer buffer = ByteBuffer.wrap(header).order(ByteOrder.LITTLE_ENDIAN); // BMP文件头(14字节) buffer.put((byte)'B'); buffer.put((byte)'M'); buffer.putInt(fileSize); buffer.putShort(reserved1); buffer.putShort(reserved2); buffer.putInt(dataBegin); // BMP信息头(40字节) buffer.putInt(headSize); buffer.putInt(width); buffer.putInt(height); buffer.putShort(layers); buffer.putShort(pixelSize); buffer.putInt(compression); buffer.putInt(imageSize); buffer.putInt(horizontalResolution); buffer.putInt(verticalResolution); buffer.putInt(colorTableSize); buffer.putInt(countMainColor); return header; } private byte[] createImage() { int rowSize = (width * 3 + 3) / 4 * 4; byte[] image = new byte[this.imageSize]; for (int y = 0; y < height; y++) { int rowStart = y * rowSize; for (int x = 0; x < width; x++) { int pixelIndex = rowStart + x * 3; // BMP像素格式:BGR顺序 image[pixelIndex] = 0; // 蓝色通道 image[pixelIndex + 1] = 0; // 绿色通道 image[pixelIndex + 2] = 0; // 红色通道 } } return image; } }
内容的提问来源于stack exchange,提问作者Julian
相关产品推荐
相关产品推荐

