首页 > 编程语言 >java.io.IOException: Permission denied——文件导出时报错

java.io.IOException: Permission denied——文件导出时报错

时间:2023-01-05 13:22:45浏览次数:48  
标签:java Permission IOException io poi Integer ARRAY 4.1

背景

业务系统中,有一个导出,就是很普通的列表查询,然后可以点击导出,生成一个文件。就这么一个功能。
使用的 excel 工具类是: org.apache.poi.xssf 用的poi这个工具类。

问题描述

在一天晚上项目上线后,发现这个功能点出现错误
点击导出时,后台报了一段错误:

Caused by: java.io.IOException: Permission denied
 at java.io.UnixFileSystem.createFileExclusively(Native Method) ~[?:1.8.0_311]
 at java.io.File.createTempFile(File.java:2061) ~[?:1.8.0_311]
 at org.apache.poi.util.DefaultTempFileCreationStrategy.createTempFile(DefaultTempFileCreationStrategy.java:110)  ~[poi-4.1.2.jar!/:4.1.2]
 at org.apache.poi.util.TempFile.createTempFile(TempFile.java:66) ~[poi-4.1.2.jar!/:4.1.2]
 at org.apache.poi.xssf.streaming.SheetDataWriter.createTempFile(SheetDataWriter.java:89) ~[poi-ooxml-4.1.2.jar!/:4.1.2]
 at org.apache.poi.xssf.streaming.SheetDataWriter.<init>(SheetDataWriter.java:72) ~[poi-ooxml-4.1.2.jar!/:4.1.2]
 at org.apache.poi.xssf.streaming.SheetDataWriter.<init>(SheetDataWriter.java:77) ~[poi-ooxml-4.1.2.jar!/:4.1.2]
 at org.apache.poi.xssf.streaming.SXSSFWorkbook.createSheetDataWriter(SXSSFWorkbook.java:342)  ~[poi-ooxml-4.1.2.jar!/:4.1.2]
 at org.apache.poi.xssf.streaming.SXSSFSheet.<init>(SXSSFSheet.java:80) ~[poi-ooxml-4.1.2.jar!/:4.1.2]
 at org.apache.poi.xssf.streaming.SXSSFWorkbook.createAndRegisterSXSSFSheet(SXSSFWorkbook.java:684)  ~[poi-ooxml-4.1.2.jar!/:4.1.2]
 ... 89 more

然后,一看啊,肯定是文件没有读写权限啊,(之前服务用 root 启动过,然后后来用了普通用户启动),方面是对的,但是我们把已知的一些路径都检查过,也没有读写权限不对的问题呀? 怎么回事呢? 真相只有一个,就是还有我们没发现的地方,存在读写权限问题!!

那怎样找到这个未被发现的路径呢?


找“未知的文件路径”

这一次还是用了 arthas来定位问题,用这个来定位出入参真的是很方便。
看报错,是在 java.io.UnixFileSystem.createFileExclusively(Native Method) ~[?:1.8.0_311] 这里抛的问题,用arthas定位一下:

watch java.io.UnixFileSystem createFileExclusively params
很可惜,arthas 在这个方法里面执行不了,因为它是一个 native 方法。(大概是这么回事,没细究)

然后只能退而求其次,去找到上一级的入口,就是: at java.io.File.createTempFile(File.java:2061) ~[?:1.8.0_311]
幸运的是,这一次可以被监听到
这一行的方法对应定义是这样子的:

    public static File createTempFile(String prefix, String suffix,
                                      File directory)
        throws IOException
    {
        if (prefix.length() < 3)
            throw new IllegalArgumentException("Prefix string too short");
        if (suffix == null)
            suffix = ".tmp";

        File tmpdir = (directory != null) ? directory
                                          : TempDirectory.location();
        // ... 省略
        return f;
    }

可以看到它的入参就三个参数.

OK,搞起:
watch java.io.File createTempFile params

然后,参数看不到,加个 -x 3 试试:
watch java.io.File createTempFile params -x 3

好了,可以看到入参了。
如下:

method=java.io.File.createTempFile location=AtExit
ts=2023-01-05 12:16:58; [cost=0.214534ms] result=@Object[][
    @String[poi-sxssf-template],
    @String[.xlsx],
    @File[
        fs=@UnixFileSystem[
            slash=@Character[/],
            colon=@Character[:],
            javaHome=@String[/usr/java/jdk1.8.0_311-amd64/jre],
            cache=@ExpiringCache[java.io.ExpiringCache@1a8ec1ec],
            javaHomePrefixCache=@ExpiringCache[java.io.ExpiringCache@7dd05f67],
            BA_EXISTS=@Integer[1],
            BA_REGULAR=@Integer[2],
            BA_DIRECTORY=@Integer[4],
            BA_HIDDEN=@Integer[8],
            ACCESS_READ=@Integer[4],
            ACCESS_WRITE=@Integer[2],
            ACCESS_EXECUTE=@Integer[1],
            SPACE_TOTAL=@Integer[0],
            SPACE_FREE=@Integer[1],
            SPACE_USABLE=@Integer[2],
            useCanonCaches=@Boolean[true],
            useCanonPrefixCache=@Boolean[true],
        ],
        path=@String[/tmp/poifiles],
        status=@PathStatus[
            INVALID=@PathStatus[INVALID],
            CHECKED=@PathStatus[CHECKED],
            $VALUES=@PathStatus[][isEmpty=false;size=2],
            name=@String[CHECKED],
            ordinal=@Integer[1],
        ],
        prefixLength=@Integer[1],
        separatorChar=@Character[/],
        separator=@String[/],
        pathSeparatorChar=@Character[:],
        pathSeparator=@String[:],
        PATH_OFFSET=@Long[16],
        PREFIX_LENGTH_OFFSET=@Long[12],
        UNSAFE=@Unsafe[
            theUnsafe=@Unsafe[sun.misc.Unsafe@3a1aa809],
            INVALID_FIELD_OFFSET=@Integer[-1],
            ARRAY_BOOLEAN_BASE_OFFSET=@Integer[16],
            ARRAY_BYTE_BASE_OFFSET=@Integer[16],
            ARRAY_SHORT_BASE_OFFSET=@Integer[16],
            ARRAY_CHAR_BASE_OFFSET=@Integer[16],
            ARRAY_INT_BASE_OFFSET=@Integer[16],
            ARRAY_LONG_BASE_OFFSET=@Integer[16],
            ARRAY_FLOAT_BASE_OFFSET=@Integer[16],
            ARRAY_DOUBLE_BASE_OFFSET=@Integer[16],
            ARRAY_OBJECT_BASE_OFFSET=@Integer[16],
            ARRAY_BOOLEAN_INDEX_SCALE=@Integer[1],
            ARRAY_BYTE_INDEX_SCALE=@Integer[1],
            ARRAY_SHORT_INDEX_SCALE=@Integer[2],
            ARRAY_CHAR_INDEX_SCALE=@Integer[2],
            ARRAY_INT_INDEX_SCALE=@Integer[4],
            ARRAY_LONG_INDEX_SCALE=@Integer[8],
            ARRAY_FLOAT_INDEX_SCALE=@Integer[4],
            ARRAY_DOUBLE_INDEX_SCALE=@Integer[8],
            ARRAY_OBJECT_INDEX_SCALE=@Integer[4],
            ADDRESS_SIZE=@Integer[8],
        ],
        serialVersionUID=@Long[301077366599181567],
        filePath=null,
        $assertionsDisabled=@Boolean[true],
    ],
]

从这一段信息里面,
有一个关键信息,就是: path=@String[/tmp/poifiles], 看到了文件路径。也正是这一个路径,我们可怜的普通用户没有权限读取得到。因为这个文件夹的归属是 root。(因为第一次起服务的时候,用了root启动,当这个文件夹不存在,就会创建,于是用root创建了,它的归属也就是 root 了)

把这个文件夹的归属改掉后,问题解决。



等等,为什么是这个路径呢? 找了一下源码,发现了么这以一个东西。

刚才的日志,再往上翻一下,会发现第三个参数: File directory 是如何被定义出来的,

// org.apache.poi.util.DefaultTempFileCreationStrategy#createTempFile
    public File createTempFile(String prefix, String suffix) throws IOException {
        this.createPOIFilesDirectory();
        File newFile = File.createTempFile(prefix, suffix, this.dir);
        if (System.getProperty("poi.keep.tmp.files") == null) {
            newFile.deleteOnExit();
        }
        return newFile;
    }

这里有一行: this.createPOIFilesDirectory();

点进去,看一眼:

    private void createPOIFilesDirectory() throws IOException {
        if (this.dir == null) {
            String tmpDir = System.getProperty("java.io.tmpdir");
            if (tmpDir == null) {
                throw new IOException("Systems temporary directory not defined - set the -Djava.io.tmpdir jvm property!");
            }

            this.dir = new File(tmpDir, "poifiles");
        }

        this.createTempDirectory(this.dir);
    }

到这里就很清晰了,

而在 linux 下面, 这个 System.getProperty("java.io.tmpdir"); 就是 /tmp

OK, 收工。

标签:java,Permission,IOException,io,poi,Integer,ARRAY,4.1
From: https://www.cnblogs.com/aaacarrot/p/17027269.html

相关文章

  • Java基础语法
    基本的dos命令打开cmd的方式开始+系统+命令提示行Win+R输入cmd打开控制台在任意的文件夹下面,按住shift+鼠标右键,在此处打开命令行窗口资源管理器的地址栏前面加上......
  • Java毕业生就业系统学生就业统计系统
    简介本项目主要是为了统计毕业生就业情况(就业方向分为四种:参加工作,考研,自主创业,待就业),教师可登入该系统查看学生就业情况,包括:考研、职业领域、工作城市,薪资等统计情况,并且......
  • Java JUC学习笔记
    1、JUC简介在Java5.0提供了java.util.concurrent(简称JUC)包,在此包中增加了在并发编程中很常用的工具类,用于定义类似于线程的自定义子系统,包括线程池,异步IO和轻量......
  • java在线视频播放系统视频网站影视网站电影电视剧播放网站源码
    简介ssm开发的视频网站。本项目主要包括了视频展示和查询功能,用户中心,积分管理,管理员管理等功能。演示视频https://www.bilibili.com/video/BV1tT4y1N7t8/?share_source......
  • java旅游日志博客系统旅行记录系统
    简介记录个人旅游动态日志的系统,也可以用来做博客系统,主页可以发布旅游日志,关注博主,给博主留言,管理评论,博文点赞,个人主页。演示视频https://www.bilibili.com/video/BV......
  • Java学习笔记
    一、Java8新特性1、HashMap​​深入浅出学Java——HashMap​​​​算法复杂度O(1),O(n),O(logn),O(nlogn)的含义​​2、ConcurrentHashMap​​HashMap?ConcurrentHashMap?相信......
  • Java个人博客系统ssh个人博客系统家乡特产家乡风景
    简介Java基于ssh开发的个人博客系统,主要有家乡特产文章,家乡风景,照片墙,留言等功能演示视频https://www.bilibili.com/video/BV1Yy4y1e7VR/?share_source=copy_web&vd_sou......
  • 如何通过Java应用程序将Word转为Excel
    平时在工作中,很多小伙伴会习惯性地将文件保存为Word文档格式,但有时会发现某些文件如果保存成Excel表格可能会更好地呈现。例如有的文本在Word文本中不如在Excel工作表编辑......
  • javascript array 数组 indexOf
    javascriptarray数组indexOf低版本的array是没有indexOf的。参考链接:https://reference.codeproject.com/javascript/Reference/Global_Objects/Array/indexOf/......
  • Java8中比较器和收集器的常用示例-排序、流转集合、分组、分区、查找最大最小值
    场景Java8新特性-Stream对集合进行操作的常用API:https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/126070657前面讲Stream的常用api。下面讲比较器和收集器......