MultipartFile 的transferTo ⽅法注意事项
前⾔ 最近⽤SpringBoot写⽂件上传功能,使⽤参数绑定之后确实是⾮常的⽅便了。但是,项⽬部署就出现了问题,搞得我⼀脸懵逼。后来,才发现是因为我使⽤了相对路径导致的,这个绝对是⼀个坑⼈的地⽅,不过也说明需要学习的东西还有很多!
案例再现我在⽇志中打印了路径的位置,显⽰是没有问题,当时⼀旦执⾏到ansferTo(filePath);就会产⽣⼀个FileNotFoundException,但是我前⾯的代码是执⾏了,并且创建了⼀个⽂件夹的。
Postman测试截图
⽇志输出@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 .ge
tParentFile ().mkdirs ();          }          // 如果路径不存在,上⾯的代码会创建路径,此时路径即已经创建好了          logger .info ("⽂件保存路径:{},是否存在:{}", filePath .getParentFile ().exists (), filePath .getParent ());          // 此处使⽤相对路径,似乎是⼀个坑!          // 相对路径:filePath          // 绝对路径:AbsoluteFile()          logger .info ("⽂件将要保存的路径:{}", filePath .getPath ());          file .transferTo (filePath );          logger .info ("⽂件成功保存的路径:{}", filePath .getAbsolutePath ());          return  "上传成功";      } catch  (Exception e ) {          logger .error (e .getMessage ());      }  }  return  "上传失败";}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
mkdirs方法
30
注意: 这⾥虽然没有什么头绪,当时观察⽇志可以发现,程序试图将⽂件保存到⼀个很奇怪的⽬录下,当是这个⽬录和前⾯那个filePath 已经没有关系了,这⾥是⼀个疑点!
执⾏之后代码所在⽬录下⾯已经创建了⼀个imgFile⽬录
imgFile⽂件夹中是空的,因为执⾏transferTo时抛出了异常
修改此处传如的参数,改为⽂件的绝对路径
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\684918a520684801b658c82020-11-27 10:1
5:06.522 ERROR 5200 --- [nio -8080-exec -1] r .controller .LearnController            : java .io .FileNotFoundException : C :\Users\Alfred\AppData
\tomcat .8080.2388870592947355119\work\Tomcat\localhost\ROOT\.\imgFile\684918a520684801b658c85a02bf9ba5.jpg (系统不到指定的路径。
1
2
3
4
5
6file .transferTo (filePath .getAbsoluteFile ());
1
上传成功!
执⾏之后代码所在⽬录下⾯已经创建了⼀个imgFile⽬录
imgFile⽂件夹中已经有了上传的图⽚
原因分析
上⾯失败与成功只是因为路径所代表的是相对路径和绝对路径的区别。这就说明是MultiparFile的transferTo⽅法有问题了。让我们加⼀个断点,调试⾛⼀波!debug!
补充⼀个debug的⼩知识:
debug tips:
step into: 单步执⾏,遇到⼦函数就进⼊并且继续单步执⾏(F5)
step over: 在单步执⾏时,在函数内遇到⼦函数时不会进⼊⼦函数内单步执⾏,⽽是将⼦函数整个执⾏完再停⽌,也就是把⼦函数整个作为⼀步(F6)
step return: 在单步执⾏到⼦函数内时,⽤step return就可以执⾏完⼦函数余下部分,并返回上⼀层。
setp out: 效果同 step return。
我这⾥只给AbsoluteFile());这⾏代码加了断点,这⾥我给出调试中最重要的两个步骤:
调试中代码的执⾏流程是:
但代码进⼊ transferTo 后,然后执⾏ this.part.path)⽅法,进⼊ write ⽅法内部,到这⾥就可以得到我们的答案了!
@Override
public void transferTo(File dest)throws IOException, IllegalStateException {
this.part.Path());
if(dest.isAbsolute()&&!ists()){
// 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.
}
}
@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);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
这个write⽅法,会判断传⼊的参数是否是相对路径,如果是相对路径,它会⾃⼰给我们拼接⼀个⽗路径! 所以你应该知道那个奇怪的路径是哪⾥来的了吧!
好了,⼤概可以理清了,这是因为transferTo的参数,如果是相对路径的话,程序会⾃⼰拼接⼀个⽗路径,因为我指定的相对路径中带有⼀个不存在的路径,如果尝试保存是会失败的。但是如果你传⼊的参数只是⼀个⽂件名,那应该就能保存成功。但是这样,取⽂件的时候,⼜会遇到问题了,你可能都不知道⽂件在哪⾥!
补充这⾥还有⼀个很有意思的地⽅,如果我的相对路径中不使⽤ . 开头,⽽只是以 / 开头,那么⼜会产⽣⼀个好玩的情况了。第⼀种情况就算刚才那样的,这⾥我们来讨论第⼆种情况,这种情况在Windows系统中还是同第⼀种⼀样的错误,但是在Linux系统中,它是可以正常执⾏的。如果你了解⼀点两个系统的知识的话,就应该知道Linux系统的根路径就是 /,所以以 / 开头的路径即是绝对路径。所以这也算是程序跨平台需要考虑的问题了,如果不了解Linux的话,你可能不会明⽩,这⾥我给出⼀个验证程序实际测试⼀下。
Windows系统和Linux系统运⾏结果不同的代码。
public class OSMain {
<span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token fu nction">main</span><span class="token punctuation">(</span>String<span class="token punctuation">[</span><span class="token punctuation">]</span> args<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
String path1 <span class="token operator">=</span> <span class="token string">"./hehe"</span><span class="token punctuation">;</span>
String path2 <span class="token operator">=</span> <span class="token string">"/haha"</span><span class="token punctuation">;</span>
File file1 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">File</span><span class="to ken punctuation">(</span>path1<span class="token punctuation">)</span><span class="token punctuation">;</span>
File file2 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">File</span><span class="to ken punctuation">(</span>path2<span class="token punctuation">)</span><span class="token punctuation">;</span>
System<span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"file1: "</span> <span class="token operator">+</span> file1 <span class="token operator">+</spa n> <span class="token string">" file1是绝对路径吗? "</span> <span class="token operator">+</span> file1<span class="token punctuation">.</span><spa n class="token function">isAbsolute</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punct uation">)</span><span class="token punctuation">;</span>
System<span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"file2: "</span> <span class="token operator">+</span> file1 <span class="token operator">+</spa n> <span class="token string">" file2是绝对路径吗? "</span> <span class="token operator">+</span> file2<span class="token punctuation">.</span><spa n class="token function">isAbsolute</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punct uation">)</span><span class="token punctuation">;</span>
<span class="token keyword">try</span> <span class="token punctuation">{<!-- --></span>
System<span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>file1<span class="token punctuation">.</span><span class="token function">getCanonicalPath</span><span class="token pu nctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
System<span class="token punctuation">.</span>out<span class="token punctuation">.</span><sp
an class="token function">println</span><span class="token punctuation">(</span>file2<span class="token punctuation">.</span><span class="token function">getCanonicalPath</span><span class="token pu nctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token cla ss-name">IOException</span> e<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
e<span class="token punctuation">.</span><span class="token function">printStackTrace</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span> C :\Users\Alfred\AppData\Local\Temp\tomcat .8080.2388870592947355119\work\Tomcat\localhost\ROOT\.\imgFile\684918a520684801b658c85a02bf9ba5
1import  java .io .File ;
import  java .io .IOException ;

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。