【报错记录】SpringBoot中MultipartFile上传报/tmp/tomcat.***.tmp (No such file or directory)

前言

我这个接口的需求大概是用户上传一个Excel文件到后端,后端解析这个Excel,并做一系列很耗时的操作。由于这个接口很耗时,因此做成了异步处理的方式,将处理完成的信息通过消息中心告诉用户,并不是立即接口返回。然而这个接口在我本地调试的时候都是好的,上线后就报错了。报错的内容大概是:

/tmp/tomcat.**************.tmp (No such file or directory)

解决过程

很明显是文件找不到,我就去/tmp目录下找,还真的没找到这个文件,我还以为是/tmp目录下的文件会被Linux自动删除导致的报错,结果并不是这样,我在application.yaml中增加上传路径设置,改成了非/tmp目录,结果还是报错!

修改multipart file默认上传临时文件路径的方式:

spring:
  servlet:
    multipart: 
      max-file-size: 1024MB
      max-request-size: 1024MB
      enabled: true
      location: /home/test

其中的spring.servlet.multipart.location就是用来设置Tomcat上传临时文件默认路径的,改完后就会上传到这个路径下

我心想应该是解决了,结果还是报错!!报的是/home/test/upload.**************.tmp (No such file or directory)。这下我是清楚了,路径确实是改了,但是还是找不到!进入文件夹后看了一下,确实没有临时文件生成!也就是说这个问题不是因为Linux自动清除/tmp目录的文件导致的,还另有原因。

感谢这篇博客帮我找到了真实原因:

异步处理MultipartFile -- No such file or directory - 青衫执卷 - 博客园

问题解决

这个问题是异步处理导致的,即Tomcat在执行完主线程后就会把这个临时文件删除,导致后面异步线程中Excel解析的时候文件已经找不到了。解决的唯一方法就是把这个文件另存为,然后处理完了手动删除。相当于Tomcat保存一个tmp文件,我再保存一个临时文件,Tomcat在这个主线程执行完了把这个tmp文件删了就删了,而我另存为的临时文件在异步线程Excel解析完了再删除就行了,互相不冲突,然后这个bug就被我解决了。

具体代码

@PostMapping({"/excelUpload"})
public DataVo excelUpload(HttpServletRequest request) {
    MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
    MultipartFile file = multipartRequest.getFile("file");

    String tmpDir = System.getProperty("java.io.tmpdir");
    if (!tmpDir.endsWith("/")) {
        tmpDir = tmpDir + "/";
    }
    String tmpFileOutPath = tmpDir + UUID.randomUUID().toString() + ".tmp";
    File tmpFile = new File(tmpFileOutPath);
    int byteSize = 8192;
    byte[] bytes = new byte[byteSize];
    try {
        InputStream inputStream = file.getInputStream();
        FileOutputStream fileOutputStream = new FileOutputStream(tmpFile);
        while (true) {
            int readSize = inputStream.read(bytes);
            if (readSize == byteSize) {
                // bytes是全的
                fileOutputStream.write(bytes);
            } else {
                // bytes不是全的,到文件末尾了,不足8192字节了
                if (readSize == -1) {
                    // INFO: DCTANT: 2022/2/10 如果已经是全部的,就不需要再写入了
                } else {
                    byte[] tmpByte = new byte[readSize];
                    for (int i = 0; i < readSize; i++) {
                        tmpByte[i] = bytes[i];
                    }
                    fileOutputStream.write(tmpByte);
                }
                fileOutputStream.flush();
                fileOutputStream.close();
                inputStream.close();
                break;
            }
        }
    } catch (Exception e) {
        e.printStackTrace();
    }

    // 为了方便理解,直接new Thread了
    new Thread(() -> {
        try {
            // 这里一段是Excel解析和执行大量业务逻辑的地方,这里就不放出了
            // 执行完业务逻辑后删除这个临时文件
            tmpFile.delete();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }).start();
    // return什么的我省去了,大家自己写吧
}

主要是这个multipartFile的处理比较麻烦,如果有Hutool可以直接输出,我这边由于没用到,因此还得自己去撸这个文件导出方法,反正这个问题就通过这个方式解决了,没再报文件找不到。

=========================================================

2022年7月27日更新:

multipartFile中的输入流输出到文件其实非常非常简单,完全不需要之前说的这么复杂,multipartFile自带一个方法transferTo,入参是一个File类型目标文件,也就是说直接new File(目标路径);然后multipartFile.transferTo(file)即可!

        File localFile = new File(savePath + multipartFile.getOriginalFilename());
        try {
            multipartFile.transferTo(localFile);
        } catch (IOException e) {
            e.printStackTrace();
        }

其中savePath需要自己赋值

你可能感兴趣的:(Linux,java,Tomcat,spring,boot,tomcat,后端)