java -- 解决InputStream不可重复读的问题
作者:opLW
目录
1.InputStream不可重复度
2.解决办法
3.最终方案
1.InputStream不可重复度
- 问题 最近在使用如下代码解析从网上读取的图片字节流时,遇到一个问题:SkImageDecoder::Factory returned null
fun decodeBitmapFromStream(bufferedIns: BufferedInputStream,reqWidth: Int,reqHeight: Int): Bitmap? {val options = BitmapFactory.Options().also {it.inJustDecodeBounds = true}BitmapFactory.decodeStream(bufferedIns, null, options)with(options) {inSampleSize = calculateSampleSize(options, reqWidth, reqHeight)inJustDecodeBounds = false}return BitmapFactory.decodeStream(bufferedIns, null, options)}
- 原因 第一次取图片尺寸的时候
bufferedIns
这个BufferedInputStream
被使用过了,再真正取图片的时候又使用了这个BufferedInputStream
,此时流的起始位置已经被移动过了,所以导致的IOException。 - 为什么字节流不可以重复读呢? 可以查看这篇文章 InputStream为什么不能被重复读取?
2.解决办法
字节流就好比一根管道,只负责传输字节,如果数据被获取了,那么管道里就没有内容了。
那么有什么解决办法呢?
- 重复读取两次
- 顾名思义。我们可以先打开一次数据流,获取图片的尺寸信息,然后再打开重新打开一次真正的生成bitmap。
- 用ByteArrayOutputStream存着
// 先用ByteArrayOutputStream存着val byteArrayOutputStream = ByteArrayOutputStream()val buffer = ByteArray(1024)var len: Intwhile (true) {len = inputStream.read(buffer)if ( len == -1 ) breakbyteArrayOutputStream.write(buffer, 0, len)}byteArrayOutputStream.flush() // 每次要使用时,进行转换val ins = ByteArrayInputStream(byteArrayOutputStream.toByteArray())
- 总结 这两个方法各有缺陷。要么需要重新读,浪费时间,要么需要用数组装着,对于大文件来说(如图片)会浪费空间。 有没有更好的方法呢?
- 重复读取两次
使用InputStream的reset和mark方法
在 StackOverFlow 上找到了一个类似的问题。里面提到要将
InputStream
重置,需要使用到mark
和reset
这两个方法。- mark方法 InputStream的mark方法有一段注释如下
Marks the current position in this input stream. A subsequent call to the reset method repositions this stream at the last marked position so that subsequent reads re-read the same bytes.
大概意思是说调用
mark
方法后,会记录一个标记。接下来第一次调用到reset
方法时,会从标记位开始读。 - reset方法 将流的的标记位置设为上一次调用mark时流所在的位置,从而可以重新读取流的数据。
- mark方法 InputStream的mark方法有一段注释如下
于是在第一次读取图片大小时,进行了
mark
操作,并在读完之后进行了reset
。.....bufferedIns.mark(0)BitmapFactory.decodeStream(bufferedIns, null, options)bufferedIns.reset().....
可惜的是遇到了另外一个错误: java.io.IOException: Mark has been invalidated
原来是
mark
方法有一个参数readlimit
,这个参数决定了你在相邻的一组mark
和reset
之间可以读多少个字节。比如:
mark(20)
表明你在下次reset
之前还可以读取20个字节。如果读取超过了20个字节,这个mark
标记就会无效,此时再调用reset
就会报错。而且并不是所有的InputStream子类都支持通过这种方式重复读取数据,在调用
mark
方法之前,应该先调用markSupported()
,该方法会返回一个bool
表示是否支持重复读取。详细的解读可以看这篇文章输入流InputStream的reset()和mark()方法注意事项那么这个
readlimit
参数应该设置为多少呢?- 第一次读取流时,只要保证能够读取到含有图片的尺寸信息的数据即可,这要求对各种格式的图片的编码有一定的了解。由于目前暂时不知道,所以此路暂时不通…?
- 如果设置的太小,会引起报错;如果设置的大一点,又不知道多大才合适。于是乎我们可以调用
InputStream.available()
方法,这个方法会返回一个预估值表示大概还有多少字节的数据可以读取。
3.最终方案
fun decodeBitmapFromStream(bufferedIns: BufferedInputStream,reqWidth: Int,reqHeight: Int): Bitmap? {val options = BitmapFactory.Options().also {it.inJustDecodeBounds = true}bufferedIns.mark(bufferedIns.available())BitmapFactory.decodeStream(bufferedIns, null, options)bufferedIns.reset()with(options) {inSampleSize = calculateSampleSize(options, reqWidth, reqHeight)inJustDecodeBounds = false}return BitmapFactory.decodeStream(bufferedIns, null, options)}
万水千山总是情,麻烦手下别留情。
如若讲得有不妥,文末留言告知我,
如若觉得还可以,收藏点赞要一起。
opLW原创七言律诗,转载请注明出处
java -- 解决InputStream不可重复读的问题相关推荐
- mysql可重复读和间隙锁_解决MySQL可重复读——详解间隙锁
间隙锁(Gap Lock)是Innodb在可重复读提交下为了解决幻读问题时引入的锁机制,(下面的所有案例没有特意强调都使用可重复读隔离级别)幻读的问题存在是因为新增或者更新操作,这时如果进行范围查询的 ...
- SpringCloud Gateway 构建全局缓存 Request 解决 body 不可重复读问题
增加全局缓存Request过滤器 /*** 全局缓存获取body请求数据(解决流不能重复读取问题)** @author Lion Li*/ @Component public class Global ...
- mysql脏读解决方案_MySQL为什么可以解决脏读和不可重复读?
一般是通过锁机制,解决掉不可重复读和幻读的问题.是不是可以通过乐观锁的问题去解决不可重复读和幻读的问题,MySQL 采用的是 MVCC 机制来解决脏读.不可重复读的问题. MVCC 英文全称是 Mui ...
- 可重复读隔离级别如何解决脏读、不可重复读
我们都知道可重复读隔离级别可以解决脏读.不可重复读.那么具体是如何解决的呢? 下面先通过实验来演示可重复读能够解决脏读.不可重复读问题,然后解释具体的原因. 环境搭建 1. 建立两个session连接 ...
- 可重复读解决了什么问题?
可重复读的核心就是一致性读:保证多次读取同一个数据时,其值都和事务开始的时候的内容是一致的,禁止读取到别的事务未提交的数据,会造成幻读. 事务更新数据的时候,只能用当前读,如果当前的记录的行锁被其他事 ...
- MySQL MVCC多版本并发控制(脏读和不可重复读解决原理)
文章目录 一.MVCC概念 二.MVCC应用于已提交读隔离级别 1. 解决脏读 2. 无法解决不可重复读 3. 无法解决幻读 三.MVCC应用于可重复读隔离级别 1. 解决脏读 2. 解决不可重复读 ...
- MySQL 可重复读隔离级别,完全解决幻读了吗?
我在上一篇文章中提到,MySQL InnoDB 引擎的默认隔离级别虽然是「可重复读」,但是它很大程度上避免幻读现象(并不是完全解决了),解决的方案有两种: 针对快照读(普通 select 语句),是通 ...
- Mysql在可重复读事务隔离级别下怎么解决幻读的
目录 前言 并发事务产生的问题 更新丢失 回滚丢失 覆盖丢失 脏读 不可重复读 幻读 快照读和当前读 幻读验证 快照读如何避免幻读 当前读如何避免幻读 可重复读隔离级别发生幻读情况 小结 前言 Mys ...
- MySQL事务的四种隔离级别,mysql中的不可重复读和幻读的区别,Repeatable read可重复读隔离级别下怎么不存在幻读问题?
1. 事务的隔离级别 1.1 read uncommited:读未提交.一个事务读到了另一个事务未提交的脏数据,称之为脏读. 1.2 read commited:读已提交.解决了脏读问题,但当前事务两 ...
最新文章
- iOS开发UIlabel篇:iOS 10 3 Label设置的中划线突然失效了
- hadoop中MapReduce中压缩的使用及4种压缩格式的特征的比较
- 在Linux命令行下运行Matlab
- ASP.NET运行环境配置
- QT的QBluetoothTransferManager类的使用
- Python 之父:别等了,Python 4.0 可能不会来了
- Spring中的后置处理器BeanPostProcessor讲解
- jQuery: 判断指针是否在某元素内 How do I check if the mouse is over an element
- ORM(Object Relational Mapping,对象/关系映射)
- 网页视频之H264打包为fmp4调研
- HDOJ--2092--整数解(水题)
- 2.matplotlib画散点图
- Ubuntu关闭cups打印机服务
- rdkit 修改分子
- Windows XP SP3 下 High Definition Audio 声卡安装方法
- uni-app实现微信小程序一键登录
- SQL语句 按年龄段分组统计人数
- 年包150万的腾讯程序员,深圳房产一千万,同学聚会只能排名第16!
- B. Ternary String
- HPUX 11iV3 LVM新变化
热门文章
- Win11开机只有鼠标显示怎么办?
- 前端技术栈-----WEB
- python编程快速上手自动化_《Python编程快速上手 让繁琐工作自动化(异步图书出品)》([美]Al Sweigart(斯维加特))【摘要 书评 试读】- 京东图书...
- 微信小程序分析送积分功能如何实现_微信小程序积分商城解决方案(一)
- 利用SUMIF和OFFSET+MATCH进行多条件求和
- Python人脸识别——从入门到工程实践
- node-addon-api 的简单使用,c++数值、字符串、对象、数组转化为js可用数据。
- Python中处理字符串的常用函数汇总【文末送书】
- 一个计算机毕业生的2012年求职之路
- ArcGis将2000国家大地坐标系转WGS84