生成验证码图片失败IOException: Can’t create cache file和NoSuchFileException: /tmp/imageio86013132132.tmp

生成验证码图片失败

javax.imageio.IIOException: Can’t create output stream!
javax.imageio.IIOException: Can’t create cache file!
java.nio.file.NoSuchFileException:/tmp/imageio860683512383612131.tmp


背景

互联网摸鱼搬砖人美好的周一从9点线上出现大面积无法登录,手机被打爆开始~

项目已经正常运行了好多天了,but 前一天晚上运维更新了服务器的openssl的漏洞,手动编译openssl

登录页面需要前端请求服务器验证码captcha接口获取图片验证码base64到前端展示,但接口返回500,无法生成验证码且登录login接口做了强校验且无法通过配置关闭图片验证码校验

软件架构:Springboot+Mybatis-Plus+Spring redis
项目架构:nginx+centos+mysql+redis


错误日志

登录页面需要前端请求服务器captcha接口获取图片验证码base64到前端展示,但是接口一直报500,搜了一下ELK日志报错如下:

javax.imageio.IIOException: Can’t create output stream!
at javax.imageio.ImageIO.write(ImageIO.java:1574) ~[?:1.8.0_201]

Caused by: javax.imageio.IIOException: Can’t create cache file!
at javax.imageio.ImageIO.createImageOutputStream(ImageIO.java:423) ~[?:1.8.0_201]
at javax.imageio.ImageIO.write(ImageIO.java:1572) ~[?:1.8.0_201]
… 86 more

Caused by: java.nio.file.NoSuchFileException: /tmp/imageio860683512383612131.tmp
at sun.nio.fs.UnixException.translateToIOException(UnixException.java:86) ~[?:1.8.0_201]
at sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:102) ~[?:1.8.0_201]
at sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:107) ~[?:1.8.0_201]
at sun.nio.fs.UnixFileSystemProvider.newByteChannel(UnixFileSystemProvider.java:214) ~[?:1.8.0_201]
at java.nio.file.Files.newByteChannel(Files.java:361) ~[?:1.8.0_201]
at java.nio.file.Files.createFile(Files.java:632) ~[?:1.8.0_201]
at java.nio.file.TempFileHelper.create(TempFileHelper.java:138) ~[?:1.8.0_201]
at java.nio.file.TempFileHelper.createTempFile(TempFileHelper.java:161) ~[?:1.8.0_201]
at java.nio.file.Files.createTempFile(Files.java:897) ~[?:1.8.0_201]
at javax.imageio.stream.FileCacheImageOutputStream.(FileCacheImageOutputStream.java:88) ~[?:1.8.0_201]
at com.sun.imageio.spi.OutputStreamImageOutputStreamSpi.createOutputStreamInstance(OutputStreamImageOutputStreamSpi.java:68) ~[?:1.8.0_201]
at javax.imageio.ImageIO.createImageOutputStream(ImageIO.java:419) ~[?:1.8.0_201]
at javax.imageio.ImageIO.write(ImageIO.java:1572) ~[?:1.8.0_201]
… 86 more


问题原因

1.运维更新服务器openssl导致linux的/tmp目录被清空,同时/tmp目录Linux也有自己的清空策略导致程序运行一段时间后生成验证码时找不到文件

2.linux环境下javax.ImageIO 默认 /tmp 文件夹作临时目录

验证码图片是通过javax.imageio.ImageIO类生成的,默认使用java.io.tmpdir配置的文件夹。Linux环境下默认/tmp文件夹。在ImageIO写流的时候会新建一个文件,文件夹名生成方式代码是Files.createTempFile,文件名生成是由TempFileHelper.generatePath生成。

ImageIO生成文件的具体执行方法,这里cacheDir.toPath()的值就是 /tmp
Files.createTempFile最总调用的是TempFileHelper.create方法,具体看下面代码片段

package javax.imageio.stream;
public class FileCacheImageOutputStream extends ImageOutputStreamImpl {

	public FileCacheImageOutputStream(OutputStream stream, File cacheDir){
		...
		if (cacheDir == null)
            this.cacheFile = Files.createTempFile("imageio", ".tmp").toFile();
        else
        	//这里cacheDir.toPath()的值就是 /tmp
            this.cacheFile = Files.createTempFile(cacheDir.toPath(), "imageio", ".tmp")
                                  .toFile();
        ...
	}
}

体生成文件夹名称是/tmp + “/imageio” + 随机数绝对值 + .tmp
比如/tmp/imageio860683512383612131.tmp

class TempFileHelper {
	//暴露在外的静态方法
	static Path createTempFile(Path dir,String prefix,String suffix,
							FileAttribute<?>[] attrs)throws IOException{
        return create(dir, prefix, suffix, false, attrs);
    }
    //createTempFile调用 create 方法 ,其中生成文件夹名称的是generatePath
    private static Path create(Path dir,String prefix,String suffix,
                               boolean createDirectory,FileAttribute<?>[] attrs){
    	.......
    	try {
                f = generatePath(prefix, suffix, dir);
            } catch (InvalidPathException e) {
                if (sm != null)
                    throw new IllegalArgumentException("Invalid prefix or suffix");
                throw e;
            }
        .........
    }
    //具体文件名生成方法
	private static Path generatePath(String prefix, String suffix, Path dir) {
        long n = random.nextLong();
        n = (n == Long.MIN_VALUE) ? 0 : Math.abs(n);
        //这里prefix="/imageio" , n=随机数绝对值 , suffix=".tmp"
        Path name = dir.getFileSystem().getPath(prefix + Long.toString(n) + suffix);
        if (name.getParent()= null)
            throw new IllegalArgumentException("Invalid prefix or suffix");
        return dir.resolve(name);
    }
}

当运维更新openssl的时候删除了/tmp文件夹后,导致生成好/tmp/imageio860683512383612131.tmp的文件夹名找不到报java.nio.file.NoSuchFileException

解决方案

java程序启动参数配置 -Djava.io.tmpdir参数并重启

为了避免Linux删除/tmp文件夹,我们在启动服务的时候指定其他的文件路径就能解决了。下面更改shell脚本

TEMP_DIR="/home/Apps/tmp"
# 检查日志路径是否存在
if [ ! -d "${TEMP_DIR}" ]; then
  mkdir "${TEMP_DIR}"
fi
function start(){
	nohup java -jar app.jar -Djava.ip.tmpdir=${TEMP_DIR}
}

具体看文件夹效果, 文件.8820结尾的就是项目的端口

[Apps@ tmp]$ pwd
/home/Apps/tmp
[Apps@ tmp]$ ll
total 24
drwxrwxr-x 2 Apps Apps 4096 Apr 30  2021 poifiles
drwxrwxr-x 2 Apps Apps 4096 Dec 28 20:47 tomcat-docbase.1190394056485531049.8820
drwxrwxr-x 3 Apps Apps 4096 Aug 17  2020 work
[Apps@ tmp]$

DONE~~~

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值