You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何无法读取BMP文件获取DPI?现有代码仅支持小尺寸JPEG

获取图像DPI:兼容BMP和JPEG的实用方案

嘿,我来帮你解决这个DPI读取的问题。你的现有代码只能处理小尺寸JPEG、读不了BMP,甚至用BufferedImage也没用,核心原因是:

  • BMP的DPI信息根本不在ImageIO的通用元数据结构里,它存在文件头的专用区域;
  • BufferedImage只存储像素数据,完全不保留文件的元数据信息,所以从它那儿拿DPI本来就行不通。

下面我给你拆解问题,再给出能同时处理JPEG和BMP的解决方案:

为什么你的代码读不了BMP?

你的代码依赖于ImageIO的javax_imageio_1.0通用元数据树,但BMP的读取器并没有把DPI信息映射到这个结构里——BMP的DPI是存在BITMAPINFOHEADER中的biXPelsPerMeter和biYPelsPerMeter字段,得直接解析文件头才能拿到。

另外,你原代码里读取垂直DPI的逻辑有个小bug:它判断的是水平DPI节点的长度,这会导致如果水平DPI存在但垂直DPI不存在时,代码逻辑出错,我也一起给你修正了。

解决方案:分格式处理

1. 专门读取BMP的DPI

直接解析BMP文件头,读取每米像素数后转换成DPI(1英寸=0.0254米,所以DPI = 每米像素数 × 0.0254):

import java.io.RandomAccessFile;
import java.io.IOException;

public class BmpDpiReader {
    public static void getBmpDpi(String filePath) throws IOException {
        try (RandomAccessFile raf = new RandomAccessFile(filePath, "r")) {
            // 跳过14字节的BITMAPFILEHEADER文件头
            raf.seek(14);
            // 读取BITMAPINFOHEADER的大小(标准是40字节)
            int infoHeaderSize = raf.readInt();
            
            if (infoHeaderSize != 40) {
                System.out.println("xDPI: -");
                System.out.println("yDPI: -");
                return;
            }
            
            // 跳过biWidth、biHeight等16字节字段,直接定位到像素密度字段
            raf.seek(14 + 4 + 16);
            // 读取biXPelsPerMeter(小端序,需要转成大端)
            int xPelsPerMeter = Integer.reverseBytes(raf.readInt());
            // 读取biYPelsPerMeter
            int yPelsPerMeter = Integer.reverseBytes(raf.readInt());
            
            // 转换为DPI并输出
            int xDPI = Math.round(xPelsPerMeter * 0.0254f);
            int yDPI = Math.round(yPelsPerMeter * 0.0254f);
            
            System.out.println("xDPI: " + (xDPI > 0 ? xDPI : "-"));
            System.out.println("yDPI: " + (yDPI > 0 ? yDPI : "-"));
        }
    }
}

2. 兼容JPEG和BMP的通用读取方法

先判断文件格式,再选择对应的读取逻辑,同时修正原JPEG读取代码的bug:

import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.metadata.IIOMetadataNode;
import javax.imageio.stream.ImageInputStream;
import java.io.File;
import java.io.IOException;
import java.util.Iterator;
import org.w3c.dom.NodeList;

public class ImageDpiReader {
    public static void getImageDpi(String filePath) throws IOException {
        File file = new File(filePath);
        String format = getFileExtension(file);
        
        // 处理BMP格式
        if ("bmp".equalsIgnoreCase(format)) {
            BmpDpiReader.getBmpDpi(filePath);
            return;
        }
        
        // 处理JPEG/JPG格式
        if ("jpeg".equalsIgnoreCase(format) || "jpg".equalsIgnoreCase(format)) {
            try (ImageInputStream iis = ImageIO.createImageInputStream(file)) {
                Iterator<ImageReader> readers = ImageIO.getImageReaders(iis);
                if (!readers.hasNext()) {
                    System.err.println("No available reader for JPEG format");
                    return;
                }
                
                ImageReader reader = readers.next();
                reader.setInput(iis);
                IIOMetadata meta = reader.getImageMetadata(0);
                IIOMetadataNode root = (IIOMetadataNode) meta.getAsTree("javax_imageio_1.0");
                
                // 读取水平DPI
                NodeList horizontalNodes = root.getElementsByTagName("HorizontalPixelSize");
                int xDPI = -1;
                if (horizontalNodes.getLength() > 0) {
                    IIOMetadataNode sizeNode = (IIOMetadataNode) horizontalNodes.item(0);
                    String pixelSize = sizeNode.getAttributes().item(0).getNodeValue();
                    xDPI = Math.round(25.4f / Float.parseFloat(pixelSize));
                }
                System.out.println("xDPI: " + (xDPI > 0 ? xDPI : "-"));
                
                // 读取垂直DPI(独立判断,修正原代码的bug)
                NodeList verticalNodes = root.getElementsByTagName("VerticalPixelSize");
                int yDPI = -1;
                if (verticalNodes.getLength() > 0) {
                    IIOMetadataNode sizeNode = (IIOMetadataNode) verticalNodes.item(0);
                    String pixelSize = sizeNode.getAttributes().item(0).getNodeValue();
                    yDPI = Math.round(25.4f / Float.parseFloat(pixelSize));
                }
                System.out.println("yDPI: " + (yDPI > 0 ? yDPI : "-"));
            }
            return;
        }
        
        // 其他格式暂不支持
        System.out.println("Unsupported file format: " + format);
    }
    
    // 获取文件扩展名
    private static String getFileExtension(File file) {
        String fileName = file.getName();
        int dotIndex = fileName.lastIndexOf('.');
        return dotIndex == -1 ? "" : fileName.substring(dotIndex + 1);
    }
}

额外说明

  • JPEG大文件问题:如果你的原代码处理大JPEG出错,建议用try-with-resources包裹ImageInputStream(如上代码所示),确保资源正确释放,避免缓存溢出。
  • BufferedImage的局限:记住,BufferedImage只是像素数据的容器,不包含任何文件元数据,所以别指望从它那儿获取DPI、EXIF这类信息。

内容的提问来源于stack exchange,提问作者Raman Kaushal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 06:57:37