目录

  • 背景
  • 问题
  • 源码分析
  • 后记

背景

我们在编写Spring Boot应用中,常会遇到文件上传问题,Spring Boot Web提供了MutipartFile的文件支持,具体和File的区别可自行上网搜索查阅。

问题

使用过程中,大部分同学可能会遇到当调用的tansferTo()方法后,再次获取file.getInputStream()方法时,就会报临时文件异常,如:


2022-12-16 11:42:21.994 ERROR 14780 --- [nio-8080-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exceptionjava.io.FileNotFoundException: C:\Users\lenovo\AppData\Local\Temp\tomcat.8080.4014709126083758502\work\Tomcat\localhost\ROOT\upload_ac92d6c4_3e50_41b2_85a7_f1a3bd33ddd8_00000000.tmp (系统找不到指定的文件。)at java.io.FileInputStream.open0(Native Method) ~[na:1.8.0_322]at java.io.FileInputStream.open(FileInputStream.java:195) ~[na:1.8.0_322]at java.io.FileInputStream.<init>(FileInputStream.java:138) ~[na:1.8.0_322]at org.apache.tomcat.util.http.fileupload.disk.DiskFileItem.getInputStream(DiskFileItem.java:198) ~[tomcat-embed-core-9.0.65.jar:9.0.65]at org.apache.catalina.core.ApplicationPart.getInputStream(ApplicationPart.java:100) ~[tomcat-embed-core-9.0.65.jar:9.0.65]at org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile.getInputStream(StandardMultipartHttpServletRequest.java:254) ~[spring-web-5.3.22.jar:5.3.22]at com.tanwei.spring.app.controllers.FileController.file(FileController.java:29) ~[classes/:na]at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_322]at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_322]at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_322]at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_322]

FileNotFoundException异常不难理解,就是文件找不到了?也就是说tansferTo()可能在传输完成后把临时文件删除了,这是肯定的,但是答案只能说是对一半,我们将一步一步的进行源码分析

源码分析

调用tansferTo()方法,Spring Boot Web默认是调用StandardMultipartHttpServletRequest.StandardMultipartFile.tansferTo()方法,如下所示:

public class StandardMultipartHttpServletRequest extends AbstractMultipartHttpServletRequest {// more code..private static class StandardMultipartFile implements MultipartFile, Serializable {// more code..public void transferTo(File dest) throws IOException, IllegalStateException {this.part.write(dest.getPath());if (dest.isAbsolute() && !dest.exists()) {FileCopyUtils.copy(this.part.getInputStream(), Files.newOutputStream(dest.toPath()));}}public void transferTo(Path dest) throws IOException, IllegalStateException {FileCopyUtils.copy(this.part.getInputStream(), Files.newOutputStream(dest));}}
}

我们主要看一下.tansferTo(File dest)这个方法里的this.part.write(dest.getPath());代码,这里的part实现是ApplicationPart,如下所示:

//
// Source code
//public class ApplicationPart implements Part {// more code ...public void write(String fileName) throws IOException {// 构建一个需要存储的文件File file = new File(fileName); // 判断文件的地址是否是一个绝对路径地址,形如D://x.txt,返回trueif (!file.isAbsolute()) {// 如果不是一个绝对路径地址,则在this.location下创建// this.location是一个临时文件对象,地址(C:\xx\Temp\tomcat.8080.3170522581386951881\work\Tomcat\localhost\ROOT)file = new File(this.location, fileName); }try {this.fileItem.write(file);} catch (Exception var4) {throw new IOException(var4);}}// more code ...
}

this.fileItem.write(file);这行代码是主要的核心代码,我们继续跟进去查看一下具体做了什么,如下所示:

//
// Source code ...
//package org.apache.tomcat.util.http.fileupload.disk;// imports ...public class DiskFileItem implements FileItem {// more code ...public void write(File file) throws Exception {// 判断文件项是否缓存在内存中的,这里我们没设置,一般都是存在上面的临时磁盘中if (this.isInMemory()) {// more code..} else {// 主要看一下这个代码块// 获取文件项的存储位置,即你上传的文件在磁盘上的临时文件File outputFile = this.getStoreLocation();if (outputFile == null) {throw new FileUploadException("Cannot write uploaded file to disk!");}// 获取文件长度this.size = outputFile.length();if (file.exists() && !file.delete()) {throw new FileUploadException("Cannot write uploaded file to disk!");}// 这里至关重要// 之所以不能再调用file.getInputStream()方法,就是在这// fileA.renameTo(fileB)方法://    1) 当fileA文件信息(包含文件名、文件路径)与fileB全部相同时,只是单纯的重命名//    2) 当fileA文件信息(特别是文件路径)与fileB不一致时,则存在重命名和剪切,这里的剪切就会把临时文件删除,并将文件复制到fileB位置// 所以,在调用file.getInputStream()时,file获取的还是原始的文件位置,调用transerTo()方法后(其实调用了renameTo()),原始文件已经不存在了// 故而抛出FileNotFoundException异常if (!outputFile.renameTo(file)) {BufferedInputStream in = null;BufferedOutputStream out = null;try {in = new BufferedInputStream(new FileInputStream(outputFile));out = new BufferedOutputStream(new FileOutputStream(file));IOUtils.copy(in, out);out.close();} finally {IOUtils.closeQuietly(in);IOUtils.closeQuietly(out);}}}}
}

后记

以上跟踪源码信息,便可以一目了然的去理解为什么会出现上面的问题描述了。在开发过程中,一个不经意的小错误,便有可能导致全局崩盘,写代码或者说在用代码的时候,我们还是要多去查看源码实现,这样才会少走弯路,完美闭坑。

【SpringBoot】MultipartFile的transferTo()方法详解相关推荐

  1. SpringBoot (6)---RestTemplate方法详解(2)

    SpringBoot (6)---RestTemplate方法详解(2) 说明 上一篇SpringBoot 2.1 | 第三篇:RestTemplate请求HTTP(1)简单运用了RestTempla ...

  2. html页面设置拦截器,SpringBoot拦截器Filter的使用方法详解

    SpringBoot拦截器Filter的使用方法详解 发布时间:2020-08-27 22:44:03 来源:脚本之家 阅读:107 作者:玉天恒 前言: 最新Servlet 3.0拦截器的使用 1. ...

  3. SpringBoot整合Log4j2以及配置详解

    文章目录 标题SpringBoot整合Log4j2以及配置详解 1.加入依赖 2.在src.java.main.resources目录下创建log4j2.xml文件 log4j2.xml文件内容如下: ...

  4. MultipartFile的transferTo方法注意事项

    前言 最近用SpringBoot写文件上传功能,使用参数绑定之后确实是非常的方便了.但是,项目部署就出现了问题,搞得我一脸懵逼.后来,才发现是因为我使用了相对路径导致的,这个绝对是一个坑人的地方,不过 ...

  5. java函数方法详解(简单易懂)

    方法(函数) 函数的组成是: 访问修饰符 返回值 函数名(形式参数) {函数内容; } 更多java函数方法详解视频课程学习地址:https://ke.qq.com/course/149432  有技 ...

  6. java中drawimage方法_canvas.drawImage()方法详解

    首先看html5.js /** @param {Element} img_elem @param {Number} dx_or_sx @param {Number} dy_or_sy @param { ...

  7. python统计csv行数_对Python 多线程统计所有csv文件的行数方法详解

    如下所示: #统计某文件夹下的所有csv文件的行数(多线程) import threading import csv import os class MyThreadLine(threading.Th ...

  8. python修改文件内容_Python批量修改文本文件内容的方法详解

    这篇文章主要介绍了Python批量修改文本文件内容的方法的相关资料,需要的朋友可以参考下 Python批量替换文件内容,支持嵌套文件夹 import os path="./" fo ...

  9. python二维元组_python中读入二维csv格式的表格方法详解(以元组/列表形式表示)

    如何去读取一个没有表头的二维csv文件(如下图所示)? 并以元组的形式表现数据: ((1.0, 0.0, 3.0, 180.0), (2.0, 0.0, 2.0, 180.0), (3.0, 0.0, ...

最新文章

  1. 数据团队做什么,看这篇就够了!
  2. 北大邮箱收件服务器,北京大学微电子学研究院邮件系统
  3. PKCS#11 in OP-TEE
  4. IronPython项目有了新负责人
  5. 文档容器iOS网络编程-iCloud文档存储编程实例
  6. python键_Python键盘按键模拟
  7. 诚之和:苹果汽车还没造出来,但数百万车将先装上它的“灵魂”
  8. webpack的copy-webpack-plugin、patterns
  9. 以太坊的单位wei是什么?
  10. c++实现的木叶忍者村管理
  11. python代码怎么变成软件_从Python代码到APP,你只需要一个小工具:GitHub已超6000星
  12. Java快递驿站项目
  13. Mac的3个个性化设置,你值得拥有!
  14. 证券公司信息化——6
  15. App性能测试过程记录
  16. 使用 canvas 和面向对象思想绘制流星动画
  17. 嵌入式系统与通用计算机系统的区别,嵌入式操作系统和通用计算机系统的区别是什么...
  18. finebi-数据更新与定时发邮件
  19. 旋转角度如何知道是顺时针还是逆时针旋转?(仅供参考,更靠谱的是旋转轴到z轴正半轴上)
  20. 2019秋招后台开发面试记录(阿里巴巴蚂蚁金服、百度、360、美团点评)

热门文章

  1. CC00027.elasticsearch——|HadoopElasticSearch.V27|——|ELK.v27|集群|QueryDSL|高亮|
  2. python 图片验证码
  3. 让IIS服务器支持PHP
  4. JAVA生成跳转指定页面并且附带参数的二维码
  5. 《JSP实用教程(第4版)》第2章学习笔记
  6. day 09文件处理
  7. C++各大有名库的介绍(一)
  8. 使用visio如何快速生成一个网格状图案,文档技巧!
  9. scratch词语接龙 电子学会图形化编程scratch等级考试四级真题和答案解析2021-6
  10. Linux下集群的搭建