作者: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重置,需要使用到markreset这两个方法。

      • 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操作,并在读完之后进行了reset

          .....bufferedIns.mark(0)BitmapFactory.decodeStream(bufferedIns, null, options)bufferedIns.reset().....
      

      可惜的是遇到了另外一个错误: java.io.IOException: Mark has been invalidated

    • 原来是mark方法有一个参数readlimit,这个参数决定了你在相邻的一组markreset之间可以读多少个字节。

      比如: 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不可重复读的问题相关推荐

  1. mysql可重复读和间隙锁_解决MySQL可重复读——详解间隙锁

    间隙锁(Gap Lock)是Innodb在可重复读提交下为了解决幻读问题时引入的锁机制,(下面的所有案例没有特意强调都使用可重复读隔离级别)幻读的问题存在是因为新增或者更新操作,这时如果进行范围查询的 ...

  2. SpringCloud Gateway 构建全局缓存 Request 解决 body 不可重复读问题

    增加全局缓存Request过滤器 /*** 全局缓存获取body请求数据(解决流不能重复读取问题)** @author Lion Li*/ @Component public class Global ...

  3. mysql脏读解决方案_MySQL为什么可以解决脏读和不可重复读?

    一般是通过锁机制,解决掉不可重复读和幻读的问题.是不是可以通过乐观锁的问题去解决不可重复读和幻读的问题,MySQL 采用的是 MVCC 机制来解决脏读.不可重复读的问题. MVCC 英文全称是 Mui ...

  4. 可重复读隔离级别如何解决脏读、不可重复读

    我们都知道可重复读隔离级别可以解决脏读.不可重复读.那么具体是如何解决的呢? 下面先通过实验来演示可重复读能够解决脏读.不可重复读问题,然后解释具体的原因. 环境搭建 1. 建立两个session连接 ...

  5. 可重复读解决了什么问题?

    可重复读的核心就是一致性读:保证多次读取同一个数据时,其值都和事务开始的时候的内容是一致的,禁止读取到别的事务未提交的数据,会造成幻读. 事务更新数据的时候,只能用当前读,如果当前的记录的行锁被其他事 ...

  6. MySQL MVCC多版本并发控制(脏读和不可重复读解决原理)

    文章目录 一.MVCC概念 二.MVCC应用于已提交读隔离级别 1. 解决脏读 2. 无法解决不可重复读 3. 无法解决幻读 三.MVCC应用于可重复读隔离级别 1. 解决脏读 2. 解决不可重复读 ...

  7. MySQL 可重复读隔离级别,完全解决幻读了吗?

    我在上一篇文章中提到,MySQL InnoDB 引擎的默认隔离级别虽然是「可重复读」,但是它很大程度上避免幻读现象(并不是完全解决了),解决的方案有两种: 针对快照读(普通 select 语句),是通 ...

  8. Mysql在可重复读事务隔离级别下怎么解决幻读的

    目录 前言 并发事务产生的问题 更新丢失 回滚丢失 覆盖丢失 脏读 不可重复读 幻读 快照读和当前读 幻读验证 快照读如何避免幻读 当前读如何避免幻读 可重复读隔离级别发生幻读情况 小结 前言 Mys ...

  9. MySQL事务的四种隔离级别,mysql中的不可重复读和幻读的区别,Repeatable read可重复读隔离级别下怎么不存在幻读问题?

    1. 事务的隔离级别 1.1 read uncommited:读未提交.一个事务读到了另一个事务未提交的脏数据,称之为脏读. 1.2 read commited:读已提交.解决了脏读问题,但当前事务两 ...

最新文章

  1. iOS开发UIlabel篇:iOS 10 3 Label设置的中划线突然失效了
  2. hadoop中MapReduce中压缩的使用及4种压缩格式的特征的比较
  3. 在Linux命令行下运行Matlab
  4. ASP.NET运行环境配置
  5. QT的QBluetoothTransferManager类的使用
  6. Python 之父:别等了,Python 4.0 可能不会来了
  7. Spring中的后置处理器BeanPostProcessor讲解
  8. jQuery: 判断指针是否在某元素内 How do I check if the mouse is over an element
  9. ORM(Object Relational Mapping,对象/关系映射)
  10. 网页视频之H264打包为fmp4调研
  11. HDOJ--2092--整数解(水题)
  12. 2.matplotlib画散点图
  13. Ubuntu关闭cups打印机服务
  14. rdkit 修改分子
  15. Windows XP SP3 下 High Definition Audio 声卡安装方法
  16. uni-app实现微信小程序一键登录
  17. SQL语句 按年龄段分组统计人数
  18. 年包150万的腾讯程序员,深圳房产一千万,同学聚会只能排名第16!
  19. B. Ternary String
  20. HPUX 11iV3 LVM新变化

热门文章

  1. Win11开机只有鼠标显示怎么办?
  2. 前端技术栈-----WEB
  3. python编程快速上手自动化_《Python编程快速上手 让繁琐工作自动化(异步图书出品)》([美]Al Sweigart(斯维加特))【摘要 书评 试读】- 京东图书...
  4. 微信小程序分析送积分功能如何实现_微信小程序积分商城解决方案(一)
  5. 利用SUMIF和OFFSET+MATCH进行多条件求和
  6. Python人脸识别——从入门到工程实践
  7. node-addon-api 的简单使用,c++数值、字符串、对象、数组转化为js可用数据。
  8. Python中处理字符串的常用函数汇总【文末送书】
  9. 一个计算机毕业生的2012年求职之路
  10. ArcGis将2000国家大地坐标系转WGS84