返回顶部
首页 > 资讯 > 后端开发 > Python >浅谈MultipartFile中transferTo方法的坑
  • 164
分享到

浅谈MultipartFile中transferTo方法的坑

2024-04-02 19:04:59 164人浏览 泡泡鱼

Python 官方文档:入门教程 => 点击学习

摘要

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

前言:最近用SpringBoot写文件上传功能,使用参数绑定之后确实是非常的方便了。

但是,项目部署就出现了问题,搞得我一脸懵逼。

后来,才发现是因为我使用了相对路径导致的,这个绝对是一个坑人的地方,不过也说明需要学习的东西还有很多!

案例再现


@PostMapping("/uploadFile")
public String uploadImg(@RequestParam("file") MultipartFile file, @RequestParam("equipmentId") String equipmentId) {
String baseDir = "./imgFile";  // 这里不能直接使用相对路径
  if (!file.isEmpty()) {
      String name = file.getOriginalFilename();
      String prefix = name.lastIndexOf(".") != -1 ? name.substring(name.lastIndexOf(".")) : ".jpg";
      String path = UUID.randomUUID().toString().replace("-", "") + prefix;
      try {
      	// 这里代码都是没有问题的
          File filePath = new File(baseDir, path);
          // 第一次执行代码时,路径是不存在的
          logger.info("文件保存路径:{},是否存在:{}", filePath.getParentFile().exists(), filePath.getParent());
          if (!filePath.getParentFile().exists()) {   // 如果存放路径的父目录不存在,就创建它。
              filePath.getParentFile().mkdirs();
          }
          // 如果路径不存在,上面的代码会创建路径,此时路径即已经创建好了
          logger.info("文件保存路径:{},是否存在:{}", filePath.getParentFile().exists(), filePath.getParent());
          // 此处使用相对路径,似乎是一个坑!
          // 相对路径:filePath
          // 绝对路径:filePath.getAbsoluteFile()
          logger.info("文件将要保存的路径:{}", filePath.getPath());
          file.transferTo(filePath);
          logger.info("文件成功保存的路径:{}", filePath.getAbsolutePath());
          return "上传成功";
      } catch (Exception e) {
          logger.error(e.getMessage());
      }
  }
  return "上传失败";
}

我在日志中打印了路径的位置,显示是没有问题,当时一旦执行到file.transferTo(filePath);就会产生一个FileNotFoundException,但是我前面的代码是执行了,并且创建了一个文件夹的。

Postman测试截图


在这里插入图片描述

日志输出

2020-11-27 10:15:06.519 INFO 5200 --- [NIO-8080-exec-1] r.controller.LearnController : 文件保存路径:false,是否存在:.\imgFile
2020-11-27 10:15:06.521 INFO 5200 --- [nio-8080-exec-1] r.controller.LearnController : 文件保存路径:true,是否存在:.\imgFile
2020-11-27 10:15:06.521 INFO 5200 --- [nio-8080-exec-1] r.controller.LearnController : 文件将要保存的路径:.\imgFile\684918a520684801b658c85a02bf9ba5.jpg
2020-11-27 10:15:06.522 ERROR 5200 --- [nio-8080-exec-1] r.controller.LearnController : java.io.FileNotFoundException: C:\Users\Alfred\AppData\Local\Temp
\Tomcat.8080.2388870592947355119\work\Tomcat\localhost\ROOT\.\imgFile\684918a520684801b658c85a02bf9ba5.jpg (系统找不到指定的路径。)

注意: 这里虽然没有什么头绪,当时观察日志可以发现,程序试图将文件保存到一个很奇怪的目录下,当是这个目录和前面那个filePath已经没有关系了,这里是一个疑点!

执行之后代码所在目录下面已经创建了一个imgFile目录

在这里插入图片描述

imgFile文件夹中是空的,因为执行transferTo时抛出了异常

在这里插入图片描述

修改此处传如的参数,改为文件的绝对路径


file.transferTo(filePath.getAbsoluteFile());

Postman测试截图

上传成功!

在这里插入图片描述

执行之后代码所在目录下面已经创建了一个imgFile目录

在这里插入图片描述

imgFile文件夹中已经有了上传的图片

在这里插入图片描述

原因分析

上面失败与成功只是因为路径所代表的是相对路径和绝对路径的区别。这就说明是MultiparFile的transferTo方法有问题了。让我们加一个断点,调试走一波!debug!

补充一个debug的小知识:

debug tips:
step into: 单步执行,遇到子函数就进入并且继续单步执行(F5)
step over: 在单步执行时,在函数内遇到子函数时不会进入子函数内单步执行,而是将子函数整个执行完再停止,也就是把子函数整个作为一步(F6)
step return: 在单步执行到子函数内时,用step return就可以执行完子函数余下部分,并返回上一层。
setp out: 效果同 step return。

我这里只给file.transferTo(filePath.getAbsoluteFile());这行代码加了断点,这里我给出调试中最重要的两个步骤:

调试中代码的执行流程是:

但代码进入 transferTo 后,然后执行 this.part.write(dest.getpath)方法,进入 write 方法内部,到这里就可以得到我们的答案了!


@Override
public void transferTo(File dest) throws IOException, IllegalStateException {
	this.part.write(dest.getPath());
	if (dest.isAbsolute() && !dest.exists()) {
		// Servlet 3.0 Part.write is not guaranteed to support absolute file paths:
		// may translate the given path to a relative location within a temp dir
		// (e.g. on Jetty whereas Tomcat and Undertow detect absolute paths).
		// At least we offloaded the file from memory storage; it'll get deleted
		// from the temp dir eventually in any case. And for our user's purposes,
		// we can manually copy it to the requested location as a fallback.
		FileCopyUtils.copy(this.part.getInputStream(), Files.newOutputStream(dest.toPath()));
	}
}
@Override
public void write(String fileName) throws IOException {
	File file = new File(fileName);
	if (!file.isAbsolute()) {
		file = new File(location, fileName);
	}
	try {
		fileItem.write(file);
	} catch (Exception e) {
		throw new IOException(e);
	}
}

这个write方法,会判断传入的参数是否是相对路径,如果是相对路径,它会自己给我们拼接一个父路径! 所以你应该知道那个奇怪的路径是哪里来的了吧!

C:\Users\Alfred\AppData\Local\Temp\tomcat.8080.2388870592947355119\work\Tomcat\localhost\ROOT\.\imgFile\684918a520684801b658c85a02bf9ba5.jpg

好了,大概可以理清了,这是因为transferTo的参数,如果是相对路径的话,程序会自己拼接一个父路径,因为我指定的相对路径中带有一个不存在的路径,如果尝试保存是会失败的。但是如果你传入的参数只是一个文件名,那应该就能保存成功。但是这样,取文件的时候,又会遇到问题了,你可能都不知道文件在哪里!

补充 一下吧

这里还有一个很有意思的地方,如果我的相对路径中不使用 . 开头,而只是以 / 开头,那么又会产生一个好玩的情况了。第一种情况就算刚才那样的,这里我们来讨论第二种情况,这种情况在windows系统中还是同第一种一样的错误,但是在linux系统中,它是可以正常执行的。如果你了解一点两个系统的知识的话,就应该知道Linux系统的根路径就是 /,所以以 / 开头的路径即是绝对路径。

所以这也算是程序跨平台需要考虑的问题了,如果不了解Linux的话,你可能不会明白,这里我给出一个验证程序实际测试一下。

Windows系统和Linux系统运行结果不同的代码。


import java.io.File;
import java.io.IOException;
public class OSMain {	
	public static void main(String[] args) {
		String path1 = "./hehe";
		String path2 = "/haha";
		File file1 = new File(path1);
		File file2 = new File(path2);		
		System.out.println("file1: " + file1 + " file1是绝对路径吗? " + file1.isAbsolute());
		System.out.println("file2: " + file1 + " file2是绝对路径吗? " + file2.isAbsolute());
		try {
			System.out.println(file1.getCanonicalPath());
			System.out.println(file2.getCanonicalPath());
		} catch (IOException e) {
			e.printStackTrace();
		}
	}	
}

Windows运行结果

在这里插入图片描述

Linux运行结果

这里需要一个Linux环境,但是我的电脑上面没有,虽然我买了一台阿里云服务器。但是为了这么小小的一段代码登陆阿里云服务器去执行,我又嫌麻烦。还好我想到了一个更加巧妙的方法!

以前,知乎上面曾经有一个问题是关于菜鸟教程的,然后菜鸟教程的作者亲自出来回答了问题,并且贴了一张图片——菜鸟教程技术结构图谱

这个图片本身其实是涉及到了很多的,但是我们这里只关注一个就是在线代码提交执行,看到那只可爱的鲸鱼了吗?对,它就是DockerDocker里面就是一个完整的操作系统,并且是Linux系统!

好了,打开 菜鸟教程–>java教程–>随便找一个运行实例,进去删除原来的代码,复制我这个代码上去执行,输出结果!嘿嘿

在这里插入图片描述

注意:

有些在线代码执行是屏蔽了某些包的,所以有的也不一定是可以执行成功的,如果这里作者对在线代码提交执行做了那种限制,我们还是只能老老实实的去Linux系统上面执行了。

不过,有时候站在巨人的肩膀上,真的是挺轻松的!

以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程网。

--结束END--

本文标题: 浅谈MultipartFile中transferTo方法的坑

本文链接: https://lsjlt.com/news/129592.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • 浅谈MultipartFile中transferTo方法的坑
    前言:最近用SpringBoot写文件上传功能,使用参数绑定之后确实是非常的方便了。 但是,项目部署就出现了问题,搞得我一脸懵逼。 后来,才发现是因为我使用了相对路径导致的,这个绝对...
    99+
    2024-04-02
  • MultipartFile中transferTo(File file)的路径问题及解决
    transferTo(File file)的路径问题 今天看到layui的文件上传的控件,就尝试了一下。简单创建了一个SpringMVC项目。记得在配置文件中注入以下Bean。 ...
    99+
    2024-04-02
  • 浅谈golang的json.Unmarshal的坑
    最近在golang业务开发时,遇到一个坑。 我们有个服务,会接收通用的interface对象,然后去给用户发消息。因此会涉及到把各个业务方传递过来的字符串,转成interface对象...
    99+
    2023-01-05
    golang json.Unmarshal
  • 浅谈StringBuilder类的capacity()方法和length()方法的一些小坑
    今天在做项目的过程中遇见一个StringBuilder.delete()删除得不到自己期望结果问题,一个截取字符串的问题,总得不到自己所期望的答案: 问题如下: stringBu...
    99+
    2024-04-02
  • 浅谈react useEffect闭包的坑
    问题代码 看一段因为useEffect导致的闭包问题代码 const btn = useRef(); const [v, setV] = useState(''); use...
    99+
    2024-04-02
  • 浅谈pytorch中stack和cat的及to_tensor的坑
    初入计算机视觉遇到的一些坑 1.pytorch中转tensor x=np.random.randint(10,100,(10,10,10)) x=TF.to_tensor(x) ...
    99+
    2024-04-02
  • 浅谈log4j的rootLogger及其他坑爹的地方
    目录log4j的rootLogger及其他坑爹地方经过试验发现!!!!对于申明appender的包rootLogger的级别设置不起作用log4j rootLogger配置log4j...
    99+
    2024-04-02
  • 浅谈Pytorch中autograd的若干(踩坑)总结
    关于Variable和Tensor 旧版本的Pytorch中,Variable是对Tensor的一个封装;在Pytorch大于v0.4的版本后,Varible和Tensor合并了,意...
    99+
    2024-04-02
  • 浅谈Java中String的常用方法
    String中常用的方法,我以代码的形式,来说明这些常用的方法。 @Test public void test1(){ //1.返回字符串的长度 ...
    99+
    2024-04-02
  • 浅谈Vue+AntDesignform表单的一些坑
    目录设置默认值的坑自定义 v-decorator 组件的坑最近在用 vue + ant 写项目发现 from 组件的坑还是比较多的 设置默认值的坑 控制台报 Warning: You...
    99+
    2024-04-02
  • 浅谈Python魔法方法
    特殊方法一览 在 Python 的学习和使用过程中, 你一定碰到过一些 特殊方法, 它们开头和结尾都有两条下划线, 也叫魔法方法 (Magic method), 或者 Dunder...
    99+
    2024-04-02
  • 浅谈java中==以及equals方法的用法
    equals 方法是 java.lang.Object 类的方法。有两种用法说明:(1)对于字符串变量来说,使用“==”和“equals()”方法比较字符串时,其比较方法不同。“==”比较两个变量本身的值,即两个对象在内存中的首地址。“eq...
    99+
    2023-05-30
    java equals方法 ava
  • 浅谈Mybatis+mysql 存储Date类型的坑
    场景: 把一个时间字符串转成Date,存进Mysql。时间天数会比实际时间少1天,也可能是小时少了13-14小时 Mysql的时区是CST(使用语句:show VARIABLES LIKE '%time_zone%'...
    99+
    2022-05-25
    Mybatis mysql 存储Date类型
  • Java 学习方法浅谈
    Java本身是一种设计的非常简单,非常精巧的语言,所以Java背后的原理也很简单,归结起来就是两点: JVM的内存管理 理解了这一点,所有和对象相关的问题统统都能解决 JVM Class Loader 理解了这一点,所有和Java相关的配置...
    99+
    2023-06-03
  • java中MultipartFile互转File的方法
    目录MultipartFile转FileFile转MultipartFile  PS:file转base64字符串MultipartFile转File 公司业务遇到需要接收...
    99+
    2024-04-02
  • 浅谈Python处理PDF的方法
    处理pdf文档 第一、 从文本中提取文本 第二、 创建PDF 两种方法 #使用PdfFileWriter import PyPDF2 pdfFiles = [] for filename in ...
    99+
    2022-06-04
    浅谈 方法 Python
  • 浅谈springboot之JoinPoint的getSignature方法
    JoinPoint的getSignature方法 在使用springboot写aop的时候,有个JoinPoint类,用来获取代理类和被代理类的信息。 这个文章记录一下JoinPoi...
    99+
    2024-04-02
  • 浅谈SpringBoot中的Bean初始化方法 @PostConstruct
    目录注解说明代码示例注解示例错误示例正确示例SpringBoot @PostConstruct虽好,也要慎用1 问题的产生2 案例模拟3 总结注解说明 使用注解: @Pos...
    99+
    2024-04-02
  • 浅谈Vue中插槽slot的使用方法
    如何定义和使用: 在组件的template中使用slot标签定义,slot标签中间可以定义默认显示值,如果slot标签没有声明name属性值,在使用插槽时将默认从第一个插槽依次往下...
    99+
    2024-04-02
  • 浅谈python中的实例方法、类方法和静态方法
    在学习python代码时,看到有的类的方法中第一参数是cls,有的是self,经过了解得知,python并没有对类中方法的第一个参数名字做限制,可以是self,也可以是cls,不过根据人们的惯用用法,sel...
    99+
    2022-06-04
    方法 浅谈 静态
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作