Spring Upload File 报错FileNotFoundException

背景

我们在项目中肯定经常遇到文件流相关的问题,本篇博文就是记录下使用文件路径不当引发的错误

环境配置

环境:
Springboot 2.0.4
JDK1.8
内嵌 Apache Tomcat/8.5.32

前端代码

前端上传网页表单,enctype 和 input 的type=file 即可,使用单文件上传举例:

<form enctype="multipart/form-data" method="POST"
    action="/file/fileUpload">
    图片<input type="file" name="file" />
    <input type="submit" value="上传" />
</form>

后端代码

@RestController
@RequestMapping("/file")
public class UploadFileController {
    @Value("${file.upload.path}")
    private String path = "upload/";

    @RequestMapping(value = "fileUpload", method = RequestMethod.POST)
    @ResponseBody
    public String fileUpload(@RequestParam("file") MultipartFile file) {
        if (file.isEmpty()) {
            return "false";
        }
        String fileName = file.getOriginalFilename();
        File saveFile = new File(path + "/" + fileName);
        if (!saveFile.getParentFile().exists()) { 
            saveFile.getParentFile().mkdirs();
        }
        try {
            file.transferTo(saveFile); // 保存文件
            return "true";
        } catch (Exception e) {
            e.printStackTrace();
            return "false";
        }
    }
}

问题分析与解决

按照上面配置运行时,在保存文件 file.transferTo(saveFile) 报错

问题原因分析:

如果是在Linux下上面的代码块是没有问题,如果在本地windows就会报错

saveFile是相对路径,指向 upload/doc20170816162034_001.jpg
file.transferTo 方法调用时,判断如果是相对路径,则使用temp目录,为父目录
因此,实际保存位置为 C:\Users\xxxx\AppData\Local\Temp\tomcat.372873030384525225.8080\work\Tomcat\localhost\ROOT\upload\doc20170816162034_001.jpg

一则,位置不对,二则没有父目录存在,因此产生上述错误。

问题解决

transferTo 传入参数 定义为绝对路径

@RestController
@RequestMapping("/file")
public class UploadFileController {
    @Value("${file.upload.path}")
    private String path = "upload/";

    @RequestMapping(value = "fileUpload", method = RequestMethod.POST)
    @ResponseBody
    public String fileUpload(@RequestParam("file") MultipartFile file) {
        if (file.isEmpty()) {
            return "false";
        }
        String fileName = file.getOriginalFilename();
        File dest = new File(new File(path).getAbsolutePath()+ "/" + fileName);
        if (!dest.getParentFile().exists()) { 
            dest.getParentFile().mkdirs();
        }
        try {
            file.transferTo(dest); // 保存文件
            return "true";
        } catch (Exception e) {
            e.printStackTrace();
            return "false";
        }
    }
}

也可以 file.getBytes() 获得字节数组,OutputStream.write(byte[] bytes)自己写到输出流中。
可以使用hutool工具优化下,不使用文件上传时,MultipartFile.transferTo() 方法报 FileNotFoundException

代码优化

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;

@RestController
@RequestMapping("/file")
public class UploadFileController {

    @Value("${file.upload.path}")
    private String basePath;

    @PostMapping("fileUpload")
    @ResponseBody
    public String fileUpload(@RequestParam("file") MultipartFile file) {
        if (file.isEmpty()) {
            return "false";
        }
        
        String fileName = file.getOriginalFilename();
        String filePath = basePath + File.separator + fileName; // 使用File.separator确保跨平台兼容性
        
        // 使用Hutool的FileUtil简化文件操作
        File saveFile = FileUtil.file(filePath);
        if (!FileUtil.exist(saveFile)) {
            FileUtil.mkdir(saveFile.getParentFile()); // 自动创建缺失的父目录
        }
        
        try {
            // 使用IoUtil.copy简化流的复制操作
            IoUtil.copy(file.getInputStream(), FileUtil.getOutputStream(saveFile), IoUtil.DEFAULT_BUFFER_SIZE);
            return "true";
        } catch (IOException e) {
            e.printStackTrace();
            return "false";
        }
    }
}

补充方法

application.properties 中增加配置项
spring.servlet.multipart.location= # Intermediate location of uploaded files.
关于上传文件的访问
增加一个自定义的ResourceHandler把目录公布出去

// 写一个Java Config 
@Configuration
public class webMvcConfig implements org.springframework.web.servlet.config.annotation.WebMvcConfigurer{
    // 定义在application.properties
    @Value("${file.upload.path}")
    private String path = "upload/";
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        String p = new File(path).getAbsolutePath() + File.separator;//取得在服务器中的绝对路径
        System.out.println("Mapping /upload/** from " + p);
        registry.addResourceHandler("/upload/**") // 外部访问地址
            .addResourceLocations("file:" + p)// springboot需要增加file协议前缀
            .setCacheControl(CacheControl.maxAge(30, TimeUnit.MINUTES));// 设置浏览器缓存30分钟
    }
}

application.properties 中 file.upload.path=upload/
实际存储目录
D:/upload/2019/03081625111.jpg
访问地址(假设应用发布在http://www.a.com/)
http://www.a.com/upload/2019/03081625111.jpg
在Controller中增加一个RequestMapping,把文件输出到输出流中

@RestController
@RequestMapping("/file")
public class UploadFileController {
    @Autowired
    protected HttpServletRequest request;
    @Autowired
    protected HttpServletResponse response;
    @Autowired
    protected ConversionService conversionService;

    @Value("${file.upload.path}")
    private String path = "upload/";    

    @RequestMapping(value="/view", method = RequestMethod.GET)
    public Object view(@RequestParam("id") Integer id){
        // 通常上传的文件会有一个数据表来存储,这里返回的id是记录id
        UploadFile file = conversionService.convert(id, UploadFile.class);// 这步也可以写在请求参数中
        if(file==null){
            throw new RuntimeException("没有文件");
        }
        
        File source= new File(new File(path).getAbsolutePath()+ "/" + file.getPath());
        response.setContentType(contentType);

        try {
            FileCopyUtils.copy(new FileInputStream(source), response.getOutputStream());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值