首页 > 其他分享 >我的ImageIO.write ByteArrayOutputStream为什么这么慢?

我的ImageIO.write ByteArrayOutputStream为什么这么慢?

时间:2022-10-24 15:06:05浏览次数:55  
标签:ImageIO stream cache write IOException File ByteArrayOutputStream null

File.createTempFile(prefix, suffix),创建一个临时文件,再使用完之后清理即可。
但是遇到如下两个坑:

String prefix = "temp";
String suffix = ".txt";
File tempFile = File.createTempFile(prefix, suffix);

以上代码中,需要注意的两个地方:
1、prefix必须大于3个字符
2、suffix需要带上 . , 比如:.png、.zip


问题来源:

      1.系统生成二维码,需要不同的图片格式来适应客户端要求

      2.图片通过接口模式给客户端,最终使用base64来传递

 

平常思考模式:

     1.BufferedImage首先通过工具把数据生成出来。

     2.我绝对不会把这个BufferedImage写磁盘,直接放内存ByteArrayOutputstream后转base64岂不是更快?

     3.ImageIO.write正好有个write(BufferedImage img,String format,OutputStream output)

     4.真的舒服,我就用它了!

 

实际情况:

    1.Linux环境centos6.8 虚拟化环境

    2.JRE1.8

    3.接口工作流程:
(1) 生成BufferedImage
(2) BufferedImage通过

ImageIO.write(BufferedImage,"png",ByteArrayOutputStream out)

(3)将ByteArrayOutputStream转化为base64
(4) 接口返回

    4.一个普通的链接,生成二维码并返回base64,接口耗时1.7S

    5.png图片大小16K

 

分析问题&尝试更换接口:

     1.一个图片生成16K,不大

     2.一次请求1.7s,又是手机端应用,太慢了!不能接受

     3.根据代码跟踪分析得出速度慢在 ImageIO.write这里

     4.网上搜索信息也有相关的反馈说ImageIO.write png的时候奇慢无比,但是没有找到实际解决方法

ImageIO.write(BufferedImage,"png",File out)

     6.测试结果:write到file后,接口响应时间在400ms!!!

 

查看源代码:

     1.对比write到Byte和File的源代码发现,使用ByteArrayOutputStream的底层写数据的时候使用了FileCacheImageOutputStream,而使用File的底层写数据的时候使用了FileImageOutputStream。

     2.查看FileCacheImageOutputStream的初始化方式、和写数据相关代码

//初始化代码
public FileCacheImageOutputStream(OutputStream stream, File cacheDir)
throws IOException {
if (stream == null) {
throw new IllegalArgumentException("stream == null!");
}
if ((cacheDir != null) && !(cacheDir.isDirectory())) {
throw new IllegalArgumentException("Not a directory!");
}
this.stream = stream;
//这里竟然创建了临时文件
if (cacheDir == null)
this.cacheFile = Files.createTempFile("imageio", ".tmp").toFile();
else
this.cacheFile = Files.createTempFile(cacheDir.toPath(), "imageio", ".tmp")
.toFile();
this.cache = new RandomAccessFile(cacheFile, "rw");

this.closeAction = StreamCloser.createCloseAction(this);
StreamCloser.addToQueue(closeAction);
}


// 写数据,没什么特殊
public void write(int b) throws IOException {
flushBits(); // this will call checkClosed() for us
cache.write(b);
++streamPos;
maxStreamPos = Math.max(maxStreamPos, streamPos);
}


//关闭
public void close() throws IOException {
maxStreamPos = cache.length();

seek(maxStreamPos);
//注意这里!!!!!
flushBefore(maxStreamPos);
super.close();
cache.close();
cache = null;
cacheFile.delete();
cacheFile = null;
stream.flush();
stream = null;
StreamCloser.removeFromQueue(closeAction);
}

//把数据写入ByteArrayOutputStream
public void flushBefore(long pos) throws IOException {
long oFlushedPos = flushedPos;
super.flushBefore(pos); // this will call checkClosed() for us

long flushBytes = flushedPos - oFlushedPos;
if (flushBytes > 0) {
// 这里使用了一个逻辑每次只读512个字节到stream里面!!然后循环
int bufLen = 512;
byte[] buf = new byte[bufLen];
cache.seek(oFlushedPos);
while (flushBytes > 0) {
int len = (int)Math.min(flushBytes, bufLen);
cache.readFully(buf, 0, len);
stream.write(buf, 0, len);
flushBytes -= len;
}
stream.flush();
}
}

 

      3.而FileImageOutputStream 的相关代码如下,都很中规中矩没有什么特殊

//初始化
public FileImageOutputStream(File f)
throws FileNotFoundException, IOException {
this(f == null ? null : new RandomAccessFile(f, "rw"));
}


//写数据
public void write(int b) throws IOException {
flushBits(); // this will call checkClosed() for us
raf.write(b);
++streamPos;
}

//关闭
public void close() throws IOException {
super.close();
disposerRecord.dispose(); // this closes the RandomAccessFile
raf = null;
}

 

分析源代码:

      1.使用了cache的方式对数据读取和写入做了优化,为了防止内存溢出他已512字节读取然后写入输出流。但是当写到ByteArrayOutputStream的时候反而显得笨拙,一个16k的图片走cache的方式需要反复读取32次。

      2.使用了普通模式的读取写入数据中规中矩,而读取因为了解文件大小都在16k左右,我采用了一次性读取到内存,所以将File类型的读取到内存再转化base64的时候,只发生了1次磁盘IO

 

结论:

    1. 我们不能被代码外表所欺骗,乍一眼觉得写内存肯定比写File要快。

    2.FileCacheImageOutputStream的出发点是好的,分批次数据读取然后写输出流

    3.ImageIO.write 下面这出代码针对ByteArrayOutputStream的策略选择有失误:

while (iter.hasNext()) {
ImageOutputStreamSpi spi = (ImageOutputStreamSpi)iter.next();
if (spi.getOutputClass().isInstance(output)) {
try {
//针对ByteArrayOutputStream输出流选择 ImageOutputStream的实现不理想
return spi.createOutputStreamInstance(output,
usecache,
getCacheDirectory());
} catch (IOException e) {
throw new IIOException("Can't create cache file!", e);
}
}
}

     磁盘缓存对ByteArray输出流没有效果,该溢出的还是会溢出,还不如直接不使用cache

     4.最终我们采用了先写图片到磁盘文件,然后读取文件转base64再返回,接口稳定在了 400ms内


 



标签:ImageIO,stream,cache,write,IOException,File,ByteArrayOutputStream,null
From: https://blog.51cto.com/u_15147537/5789779

相关文章

  • ABC246F typewriter 题解
    ABC246FtypewriterSolution目录ABC246FtypewriterSolution更好的阅读体验戳此进入题面SolutionCodeUPD更好的阅读体验戳此进入题面给定$n$个字符串,字符集为小......
  • CopyOnWriteArrayList与CopyOnWriteArraySet详解
    什么是CopyOnWrite容器【1】CopyOnWrite容器是基于并发模式Copy-on-Write模式(最简单的并发解决方案)实现的用于避免共享的数据集合。【2】CopyOnWrite容器又被成......
  • CopyOnWriteArrayList集合
    CopyOnWriteArrayList是为了增加在写操作的时候的读操作的性能因为并发问题主要是写操作,当一个线程进行写操作时,会使用Reetranlock加锁,然后会复制一份原数组在新数组上进......
  • 【sysbench】read_write测试方法与脚本
    测试工具Sysbench是一个基于LuaJIT的可编写脚本的多线程基准测试工具。它最常用于数据库基准测试,但也可用于创建不涉及数据库服务器的任意复杂工作负载,本次测试将采用Sys......
  • CCS 2022 极客少年挑战赛 writeup
    ​  目录题目一DSDS操作内容:题目二easy_re操作内容:flag值:题目三1+1=all   解题过程题目一DSDS操作内容:开环境然后进入网址在网址后./目录进入......
  • NGINX配置文件,REWRITE,IF
    NGINX配置文件,REWRITE,IF目录NGINX配置文件,REWRITE,IFREWRITIF基于浏览器实现分离案例防盗链案例nginx配置文件,rewrite,if[rewrit]if[基于浏览器实现分离案例][防盗......
  • 【go】 golang 的文件写入和读取(Write()、WriteString(),bufio包:WriteString()、flush(
    1.os包1.1Write()/WriteString()方法语法打开文件funcOpenFile(namestring,flagint,permFileMode)(*File,error)写入func(f*File)WriteString(sstr......
  • nginx配置文件rewrite和if
    nginx配置文件rewrite和ifrewrite语法:rewriteregexreplacementflag;,如:rewrite^/images/(.*\.jpg)$/imgs/$1break;此处的$1用于引用(.*.jpg)匹配到的内容,又如:re......
  • nginx配置文件rewrite和if
    nginx配置文件rewrite和ifrewrite语法:rewriteregexreplacementflag;,如:rewrite^/images/(.*\.jpg)$/imgs/$1break;此处的$1用于引用(.*.jpg)匹配到的内容,又如:re......
  • C#使用StreamReader类和StreamWriter类读写文本文件
    StreamReader类和StreamWriter类可以实现读写文本文件,这两个类都在命名空间System.IO下。usingSystem;usingSystem.Collections.Generic;usingSystem.ComponentModel;usi......