关于在Java 程序中如何根据上传的图片流信息,获取上传的图片类型、宽、高这个问题一直纠结着我,终于抽出时间对这个问题分析
在JDK 中有提供现成的API 来查询图片的类型、宽、高。
ImageInputStream imageInputStream = ImageIO.createImageInputStream(new File("D:\1.jpg")); Iterator<ImageReader> iter = ImageIO.getImageReaders(imageInputStream); if (null != iter && iter.hasNext()) { ImageReader reader = iter.next(); format = reader.getFormatName(); //获得图片的类型 reader.setInput(imageInputStream, true); reader.getWidth(0); //获得图片的宽 reader.getHeight(0); //获得图片的高 }
获取图片类型
重要源码分析
1、ImageIO.getImageReaders()方法,参数是ImageInputStream 或者其他。
/** * Returns an <code>Iterator</code> containing all currently * registered <code>ImageReader</code>s that claim to be able to * decode the supplied <code>Object</code>, typically an * <code>ImageInputStream</code>. * * <p> The stream position is left at its prior position upon * exit from this method. * * @param input an <code>ImageInputStream</code> or other * <code>Object</code> containing encoded image data. * * @return an <code>Iterator</code> containing <code>ImageReader</code>s. * * @exception IllegalArgumentException if <code>input</code> is * <code>null</code>. * * @see javax.imageio.spi.ImageReaderSpi#canDecodeInput */ public static Iterator<ImageReader> getImageReaders(Object input) { if (input == null) { throw new IllegalArgumentException("input == null!"); } Iterator iter; // Ensure category is present try { iter = theRegistry.getServiceProviders(ImageReaderSpi.class, new CanDecodeInputFilter(input), true); //读取项目 } catch (IllegalArgumentException e) { return new HashSet().iterator(); } return new ImageReaderIterator(iter); }
仔细观察ImageReader对象发现它是一个抽象类,在抽象类中存在一个抽象的属性ImageReaderSpi originatingProvider 。其实这个属性的实现类具有以下几种继承类:
BMPImageReaderSpi 、GIFImageReaderSpi、JPEGImageReaderSpi、PNGImageReaderSpi、WBMPImageReaderSpi ,这个抽象属性就是读取图片流信息的关键。
1.1 IIORegistry.getServiceProviders()方法用以获得ImageReader的确切实现类,参数1:抽象类的源文件,用于获取继承抽象类的集合,参数2:对继承抽象类的集合的过滤器CanDecodeInputFilter,参数3:boolean型 返回的结果是否是Key/Value返回结果。
/** * Returns an <code>Iterator</code> containing service provider * objects within a given category that satisfy a criterion * imposed by the supplied <code>ServiceRegistry.Filter</code> * object's <code>filter</code> method. * * <p> The <code>useOrdering</code> argument controls the * ordering of the results using the same rules as * <code>getServiceProviders(Class, boolean)</code>. * * @param category the category to be retrieved from. * @param filter an instance of <code>ServiceRegistry.Filter</code> * whose <code>filter</code> method will be invoked. * @param useOrdering <code>true</code> if pairwise orderings * should be taken account in ordering the returned objects. * * @return an <code>Iterator</code> containing service provider * objects from the given category, possibly in order. * * @exception IllegalArgumentException if there is no category * corresponding to <code>category</code>. */ public <T> Iterator<T> getServiceProviders(Class<T> category, Filter filter, boolean useOrdering) { SubRegistry reg = (SubRegistry)categoryMap.get(category); if (reg == null) { throw new IllegalArgumentException("category unknown!"); } Iterator iter = getServiceProviders(category, useOrdering); return new FilterIterator(iter, filter); }
getServiceProviders()方法用以获得ImageReaderSpi继承子类的迭代器BMPImageReaderSpi 、GIFImageReaderSpi、JPEGImageReaderSpi、PNGImageReaderSpi、WBMPImageReaderSpi的集合,这个方法是公用方法,暂未研究透彻因此不在这里丢人。
new FilterIterator(iter, filter);是调用传进来的CanDecodeInputFilter过滤器的filter方法进行过滤。代码如下:
public FilterIterator(Iterator<T> iter, ServiceRegistry.Filter filter) { this.iter = iter; this.filter = filter; advance(); } private void advance() { while (iter.hasNext()) { T elt = iter.next(); if (filter.filter(elt)) { next = elt; return; } } next = null; }
filter.filter()方法如下所示:
static class CanDecodeInputFilter implements ServiceRegistry.Filter { Object input; public CanDecodeInputFilter(Object input) { this.input = input; } public boolean filter(Object elt) { try { ImageReaderSpi spi = (ImageReaderSpi)elt; ImageInputStream stream = null; if (input instanceof ImageInputStream) { stream = (ImageInputStream)input; } // Perform mark/reset as a defensive measure // even though plug-ins are supposed to take // care of it. boolean canDecode = false; if (stream != null) { stream.mark(); } canDecode = spi.canDecodeInput(input); if (stream != null) { stream.reset(); } return canDecode; } catch (IOException e) { return false; } } }
CanDecodeInputFilter 过滤器会对ImageReaderSpi子类集合进行迭代循环并调用各自的重写canDecodeInput方法对stream进行检测是那种类型(stream是在new CanDecodeInputFilter时传输进来的ImageInputStream)。
接下来我们来看下ImageReaderSpi的子类中对canDecodeInput方法的内容
1)BMPImageReaderSpi.canDecodeInput
public boolean canDecodeInput(Object source) throws IOException { if (!(source instanceof ImageInputStream)) { return false; } ImageInputStream stream = (ImageInputStream)source; byte[] b = new byte[2]; stream.mark(); stream.readFully(b); stream.reset(); return (b[0] == 0x42) && (b[1] == 0x4d); }
2)GIFImageReaderSpi.canDecodeInput
public boolean canDecodeInput(Object input) throws IOException { if (!(input instanceof ImageInputStream)) { return false; } ImageInputStream stream = (ImageInputStream)input; byte[] b = new byte[6]; stream.mark(); stream.readFully(b); stream.reset(); return b[0] == 'G' && b[1] == 'I' && b[2] == 'F' && b[3] == '8' && (b[4] == '7' || b[4] == '9') && b[5] == 'a'; }
3)JPEGImageReaderSpi.canDecodeInput
public boolean canDecodeInput(Object source) throws IOException { if (!(source instanceof ImageInputStream)) { return false; } ImageInputStream iis = (ImageInputStream) source; iis.mark(); // If the first two bytes are a JPEG SOI marker, it's probably // a JPEG file. If they aren't, it definitely isn't a JPEG file. int byte1 = iis.read(); int byte2 = iis.read(); iis.reset(); if ((byte1 == 0xFF) && (byte2 == JPEG.SOI)) { return true; } return false; }
4)PNGImageReaderSpi.canDecodeInput
public boolean canDecodeInput(Object source) throws IOException { if (!(source instanceof ImageInputStream)) { return false; } ImageInputStream stream = (ImageInputStream)source; byte[] b = new byte[3]; stream.mark(); stream.readFully(b); stream.reset(); return ((b[0] == (byte)0) && // TypeField == 0 b[1] == 0 && // FixHeaderField == 0xxx00000; not support ext header ((b[2] & 0x8f) != 0 || (b[2] & 0x7f) != 0)); // First width byte //XXX: b[2] & 0x8f) != 0 for the bug in Sony Ericsson encoder. }
5)WBMPImageReaderSpi.canDecodeInput
public boolean canDecodeInput(Object source) throws IOException { if (!(source instanceof ImageInputStream)) { return false; } ImageInputStream stream = (ImageInputStream)source; byte[] b = new byte[3]; stream.mark(); stream.readFully(b); stream.reset(); return ((b[0] == (byte)0) && // TypeField == 0 b[1] == 0 && // FixHeaderField == 0xxx00000; not support ext header ((b[2] & 0x8f) != 0 || (b[2] & 0x7f) != 0)); // First width byte //XXX: b[2] & 0x8f) != 0 for the bug in Sony Ericsson encoder. }
重点说明:看到这里就应该明白了,相同图片类型的图片流内容中的固定多少位到多少位将会是相同的,具体图盘类型的公式可以如上所示。
1.2 new ImageReaderIterator(iter)方法,只是将解析出来的ImageReaderSpi 赋值到ImageReader对象的 originatingProvider 属性中。
2、ImageReader.getFormatName(); 方法用以读出图片的类型,
public String getFormatName() throws IOException { return originatingProvider.getFormatNames()[0]; }
上面的1如果看懂了这里就非常好理解了,只是调用第一步中获得ImageReaderSpi 子类中getFormatNames方法默认取第一个值。
说到这里我们就可以自己总结出一个公用方法来根据ImageInputStream来获取图片的类型信息(当然方法作用不大,不推荐使用,只是用于学习)。
返回的就是图片的类型信息
private static String checkImageType(ImageInputStream stream){ String fileType="jpg"; byte[] b; try { b = new byte[8]; stream.mark(); stream.readFully(b); stream.reset(); int byte1 = stream.read(); int byte2 = stream.read(); if(b[0] == 'G' && b[1] == 'I' && b[2] == 'F' && b[3] == '8' && (b[4] == '7' || b[4] == '9') && b[5] == 'a'){ fileType ="GIF"; }else if((b[0] == 0x42) && (b[1] == 0x4d)){ fileType="BMP"; }else if((byte1 == 0xFF) && (byte2 == JPEG.SOI)){ fileType="JPG"; }else if((b[0] == (byte)137 && b[1] == (byte)80 && b[2] == (byte)78 && b[3] == (byte)71 && b[4] == (byte)13 && b[5] == (byte)10 && b[6] == (byte)26 && b[7] == (byte)10)){ fileType ="PNG"; }else if(((b[0] == (byte)0) && b[1] == 0 && ((b[2] & 0x8f) != 0 || (b[2] & 0x7f) != 0))){ fileType = "wbmp"; } } catch (Exception e) { e.printStackTrace(); } return fileType; }
注:以上代码获得的“图片类型”并不是图片的后缀名信息,是图片的实际类型信息。
写到这里实际只是在做JDK源码分析,在实际应用中我们都已经获得了ImageInputStream,恐怕没人会闲着没事的重写JDK的提供好的方法 reader.getFormatName()获得图片类型的方法。
下面说一下我目前所应用场景,了解Spring MVC的就会对其中的MultipartFile感到熟悉,它是在Spring MVC中默认接受图片上传流的参数,在MultipartFile.getBytes() 方法获取的byte[]其实和ImageInputStream 所得到的byte[]是完全相同的,所以我们就完全可以使用MultipartFile.getBytes() 直接进行数组读取判断获取图片类型,代码如下所示:
private String readPictureType(MultipartFile file) throws IOException { byte[] buffer = Arrays.copyOf(file.getBytes(), 8); String fileType="JPG"; if(buffer[0] == 'G' && buffer[1] == 'I' && buffer[2] == 'F' && buffer[3] == '8' && (buffer[4] == '7' || buffer[4] == '9') && buffer[5] == 'a'){ fileType ="GIF"; }else if((buffer[0] == 0x42) && (buffer[1] == 0x4d)){ fileType="BMP"; }else if((buffer[0] == (byte)137 && buffer[1] == (byte)80 && buffer[2] == (byte)78 && buffer[3] == (byte)71 && buffer[4] == (byte)13 && buffer[5] == (byte)10 && buffer[6] == (byte)26 && buffer[7] == (byte)10)){ fileType ="PNG"; }else{ fileType="JPG"; } return fileType; }
从而避免繁琐的IO流的操作,节省效率。
相关推荐
本文将基于“Java网络爬虫(蜘蛛)源码-zhizhu”这一资源,详细介绍其核心功能及实现原理,并探讨如何利用该源码进行自定义开发。 #### Java网络爬虫概述 Java网络爬虫主要负责自动地从网页上抓取数据,并将其存储为...
- **AI模型**: 识别功能可能采用了深度学习模型,如卷积神经网络(CNN),训练于大量的动植物图片数据集,以实现精准识别。 3. **开发环境**: 根据“所需开发环境.txt”文件,开发者可能使用了Eclipse或IntelliJ...
资源名字:基于Springboot+mysql的智能热度分析和自媒体推送平台设计与实现(源码+部署说明+视频演示).zip 资源内容:项目全套源码+完整文档 源码说明: 全部项目源码都是经过测试校正后百分百成功运行。 基于...
系统核心功能是通过收集与分析用户行为、活动及偏好等大量数据,运用基于内容的过滤和协同过滤技术,预测用户的潜在兴趣点,进而推送最符合用户喜好的学习内容。推荐系统不仅包括个性化的TopN推荐,还涵盖了最新和最...
系统集成了公告信息、疫情资讯、个人中心、后台管理、留言板及首页等多个功能模块,实现了信息的实时更新、用户行程的精准追踪以及用户留言的及时处理。该系统支持用户、采集员和分析员三种角色,分别拥有不同的功能...
1. 车牌定位:SDK首先对输入的视频或图片进行预处理,通过图像分析技术找到可能包含车牌的区域。 2. 车牌识别:定位到车牌后,SDK利用深度学习模型对车牌进行识别,提取出车牌号码。 3. 车牌校验:识别出的车牌...
这个压缩包文件“Android项目应用源码-高仿墨迹天气源码.zip”包含了一个针对Android平台的开发项目,其目标是实现一个高度模仿墨迹天气应用的功能。墨迹天气是一款广受欢迎的天气预报应用,以其简洁的设计和精准的...
7. **API接口**:为了与其他系统集成,源码可能设计了对外的API接口,如与地图服务提供商对接获取精准的地理信息,或者与第三方平台同步房源数据。 在分析和学习这个源码时,我们还需要关注代码的可读性、可维护性...
虹软的离线SDK提供了一整套本地化的解决方案,避免了云端服务的网络依赖,可以实现快速、稳定的人脸识别。 在该Demo中,人脸注册是实现人脸识别的第一步。注册过程通常包括捕获人脸图像、检测人脸特征、以及存储...
本篇将基于"安卓Android源码——Weather.zip"这一主题,深入探讨安卓应用程序开发的核心知识,特别是如何理解和分析一个天气应用的源代码。 首先,我们要明白Android系统的架构,它主要由Linux内核、运行库层、应用...
这个源码可能是用一种或多种编程语言实现的,例如常见的Web开发语言如PHP、Java、Python或.NET等。源码可能包含前端用户界面和后端服务器逻辑,用于处理用户注册、职位发布、简历投递等功能。 1. **用户注册与登录*...
轮播图管理:在系统首页设置重要公告或学习资源的图片轮播,帮助用户快速获取系统动态和最新资讯,增强用户体验。 系统公告管理:管理员可以发布系统公告,如考试通知、课程更新或其他重要信息。公告可以被精准推送...
### 基于Python+Vue的网上购物商城源码数据库 #### 项目背景与意义 随着互联网技术的迅速发展,电子商务已经成为人们日常生活中不可或缺的一部分。它不仅为消费者提供了丰富的商品选择,还极大地节省了购物时间。...
消息推送在IT行业中是至关重要的一个领域,尤其是在移动应用开发中。...通过学习和实践“JavaApk源码说明.txt”及类似资源,开发者可以更好地理解和掌握消息推送的核心技术,从而提升应用的用户体验和互动性。
"安卓墨迹天气预报"是一款专门为Android平台设计的天气应用,提供精准的气象信息和预报服务。这款应用因其简洁易用的界面和丰富的功能而受到用户喜爱。在深入探讨其技术实现之前,我们先来看看与之相关的文件内容。 ...
通过阅读和分析源码,可以深入理解整个APP的实现机制,这对于提升个人的Android开发技能和了解实际项目流程非常有帮助。同时,这也是一个良好的实践案例,展示了如何将各种技术集成到一个实际项目中,为类似需求的...
在Android中,获取用户位置通常通过集成Google Play服务中的`FusedLocationProviderClient`来实现。这个API能够高效地合并来自GPS、Wi-Fi、移动网络等多种定位源的信息,提供精准的定位服务。开发者需要在...
【商城小程序】是一款功能完善的商场类小程序源码,它涵盖了前端和后端的完整实现,旨在为用户提供一种高效、便捷的在线购物体验。这款小程序在设计上注重用户体验,样式精美,功能丰富,能够满足商家与消费者的各种...
- `src`目录:包含了示例应用的Java源码,通过阅读这些代码,我们可以了解如何初始化SDK、获取定位信息、上传轨迹、设置地图等操作。 总的来说,BaiduTrace_AndroidSDK_v3_0_8为开发者提供了一套完整的轨迹服务解决...
此外,还可以利用大数据分析,为用户提供更精准的诗词推荐,甚至搭建社区功能,让用户可以分享自己的诗词心得,形成互动的学习氛围。 总的来说,基于Android的诗词鉴赏APP是一个集传统文化与现代科技于一体的创新...