设为首页收藏本站
网站公告 | 这是第一条公告
     

 找回密码
 立即注册
缓存时间07 现在时间07 缓存数据 没有什么事情是简单轻松的,唯有不断努力,才能更加顺利。

没有什么事情是简单轻松的,唯有不断努力,才能更加顺利。

查看: 858|回复: 1

Java中的MultipartFile接口和File类解读

[复制链接]

  离线 

TA的专栏

等级头衔

等級:晓枫资讯-列兵

在线时间
0 小时

积分成就
威望
0
贡献
22
主题
22
精华
0
金钱
76
积分
44
注册时间
2023-9-30
最后登录
2025-2-17

发表于 2025-2-17 21:12:13 | 显示全部楼层 |阅读模式
目录
  • 一、File类
  • 二、MultipartFile接口
    • 2.1 源码和方法功能
    • 2.2 void transferTo(File dest)
      • CommonsMultipartFile中的方法体
      • StandardMultipartHttpServletRequest实现类
      • default void transferTo(Path dest)
    • 实际项目使用文件上传和下载
      • 文件上传接口
      • 文件下载接口
      • 响应处理
  • 总结

    一、File类

    java.io.File是 Java 标准库中用于操作文件和目录路径的类。它提供了很多方法,用于创建、删除、重命名、判断文件是否存在、获取文件信息等操作。

    获取文件信息

      1. boolean exists()
      复制代码
      : 判断文件或目录是否存在。
      1. boolean isFile()
      复制代码
      : 判断是否是文件。
      1. boolean isDirectory()
      复制代码
      : 判断是否是目录。
      1. String getName()
      复制代码
      : 获取文件或目录的名称。
      1. String getPath()
      复制代码
      : 获取文件或目录的路径。
      1. String getAbsolutePath()
      复制代码
      : 获取文件或目录的绝对路径。
      1. long length()
      复制代码
      : 获取文件的大小(字节数)。

    文件和目录操作

      1. boolean createNewFile()
      复制代码
      : 创建新文件。如果文件已存在,则不创建,返回 false。
      1. boolean mkdir()
      复制代码
      : 创建新目录。如果目录已存在,则不创建,返回 false。
      1. boolean mkdirs()
      复制代码
      : 创建新目录及其父目录,如果不存在的话。
      1. boolean delete()
      复制代码
      : 删除文件或目录。

    文件路径操作

      1. boolean renameTo(File dest)
      复制代码
      : 重命名文件或目录。如果成功,返回 true;否则,返回 false。
      1. String[] list()
      复制代码
      : 返回目录下的文件和目录名数组。
      1. File[] listFiles()
      复制代码
      : 返回目录下的文件和目录的 File 对象数组。

    文件过滤

      1. String[] list(FilenameFilter filter)
      复制代码
      : 返回目录下满足指定过滤器条件的文件和目录名数组。
      1. File[] listFiles(FileFilter filter)
      复制代码
      : 返回目录下满足指定过滤器条件的文件和目录的 File 对象数组。

    二、MultipartFile接口

    MultipartFile是 Spring 框架提供的一个接口,用于表示处理文件上传的对象。

    它通常用于处理

    1. multipart/form-data
    复制代码
    类型的请求,例如处理文件上传的表单。

    首先我们依旧可以通过源码的学习来进一步了解这个接口。

    2.1 源码和方法功能

    1. public interface MultipartFile extends InputStreamSource {
    2. String getName();
    3. @Nullable
    4. String getOriginalFilename();
    5. @Nullable
    6. String getContentType();
    7. boolean isEmpty();
    8. long getSize();
    9. byte[] getBytes() throws IOException;
    10. InputStream getInputStream() throws IOException;
    11. default Resource getResource() {
    12. return new MultipartFileResource(this);
    13. }
    14. void transferTo(File dest) throws IOException, IllegalStateException;
    15. default void transferTo(Path dest) throws IOException, IllegalStateException {
    16. FileCopyUtils.copy(this.getInputStream(), Files.newOutputStream(dest));
    17. }
    18. }
    复制代码
      1. String getName()
      复制代码
      :获取上传文件的表单字段名称
      1. String getOriginalFilename()
      复制代码
      :获取上传文件的原始文件名
      1. String getContentType()
      复制代码
      :获取上传文件的内容类型
      1. boolean isEmpty()
      复制代码
      :判断上传文件是否为空
      1. long getSize()
      复制代码
      :获取上传文件的大小,单位是字节
      1. byte[] getBytes() throws IOException
      复制代码
      :获取上传文件的字节数组表示
      1. InputStream getInputStream() throws IOException
      复制代码
      :获取上传文件的输入流
      1. default Resource getResource()
      复制代码
      :将 MultipartFile 封装成了 Resource 对象,从而可以使用 Resource 接口提供的方法来操作上传文件的内容。
      1. void transferTo(File dest) throws IOException
      复制代码
      ,
      1. IllegalStateException
      复制代码
      :将上传文件保存到指定的文件;
      1. default void transferTo(Path dest) throws IOException
      复制代码
      ,
      1. IllegalStateException
      复制代码
      :将上传文件保存在指定的路径下;

    2.2 void transferTo(File dest)

    前面我们已经介绍了该方法是Spring中提供的将上传文件保存到指定的文件中的抽象方法,溯源源码我们可以看到这个接口方法被三个实现类实现了,分别是CommonsMultipartFile、MockMultipartFile 和 StandardMultipartHttpServletRequest。

    CommonsMultipartFile中的方法体

    我们可以看到CommonsMultipartFile中的方法体主要是通过检测传进来的文件是否可用、是否存在,并在检测完成就执行写入的操作

    1. public void transferTo(File dest) throws IOException, IllegalStateException {
    2. if (!this.isAvailable()) {
    3. throw new IllegalStateException("File has already been moved - cannot be transferred again");
    4. } else if (dest.exists() && !dest.delete()) {
    5. throw new IOException("Destination file [" + dest.getAbsolutePath() + "] already exists and could not be deleted");
    6. } else {
    7. try {
    8. this.fileItem.write(dest);
    9. LogFormatUtils.traceDebug(logger, (traceOn) -> {
    10. String action = "transferred";
    11. if (!this.fileItem.isInMemory()) {
    12. action = this.isAvailable() ? "copied" : "moved";
    13. }
    14. return "Part '" + this.getName() + "', filename '" + this.getOriginalFilename() + "'" + (traceOn ? ", stored " + this.getStorageDescription() : "") + ": " + action + " to [" + dest.getAbsolutePath() + "]";
    15. });
    16. } catch (FileUploadException var3) {
    17. throw new IllegalStateException(var3.getMessage(), var3);
    18. } catch (IOException | IllegalStateException var4) {
    19. throw var4;
    20. } catch (Exception var5) {
    21. throw new IOException("File transfer failed", var5);
    22. }
    23. }
    24. }
    复制代码

    上面这段demo中可能对于this.isAvailable()有疑问,我们知晓这里的this其实是该类的实例化对象,但是这里的this.isAvailable()就是拿来判断目的文件是否可用,调用的就是类的内部方法,判断是否可用的条件就是该目标文件是否被加载进内存中

    1.jpeg

    这里给出一个使用该方法的例子,需要注意的是这时候方法要求的是一个目标File对象,我们需要在调用该目标方法的时候就根据目标路径创建了目标的File对象。

    1. // 获取上传文件的原始文件名
    2. String originalFilename = StringUtils.cleanPath(file.getOriginalFilename());
    3. // 构建目标文件对象
    4. File destFile = new File("/path/to/destination/directory", originalFilename);
    5. try {
    6. // 将上传文件保存到目标文件
    7. file.transferTo(destFile);
    8. return "File uploaded successfully!";
    9. } catch (IOException e) {
    10. e.printStackTrace();
    11. return "Failed to upload the file.";
    12. }
    复制代码

    StandardMultipartHttpServletRequest实现类

    而另一个实现类StandardMultipartHttpServletRequest和CommonsMultipartFile的区别就在于使用StandardMultipartHttpServletRequest直接上传文件的话可能会出现目录跳跃的问题,而CommonsMultipartFile不会,这是因为其对路劲分隔符了相关的限制。

    2.jpeg

    default void transferTo(Path dest)

    该默认方法在实现类中被重写了,但主要的功能还是不变,就是将上传的文件写入到指定路径的Path对象中实现文件上传的功能。

    这里给出使用的示例代码:

    1. // 构建目标文件路径
    2. String uploadDirectory = "/path/to/destination/directory";
    3. String originalFilename = file.getOriginalFilename();
    4. Path filePath = Paths.get(uploadDirectory, originalFilename);
    5. try {
    6. // 将上传文件保存到目标文件
    7. file.transferTo(filePath);
    8. return "File uploaded successfully!";
    9. } catch (IOException e) {
    10. e.printStackTrace();
    11. return "Failed to upload the file.";
    12. }
    复制代码

    实际项目使用文件上传和下载

    1. @PostMapping("/file/upload")
    2. public Result uploadFile(MultipartFile file) {
    3. String originalFilename = file.getOriginalFilename();
    4. if (StrUtil.isBlank(originalFilename)) {
    5. return Result.error("文件上传失败");
    6. }
    7. long flag = System.currentTimeMillis();
    8. String filePath = BASE_FILE_PATH + flag + "_" + originalFilename;
    9. try {
    10. FileUtil.mkParentDirs(filePath); // 创建父级目录
    11. file.transferTo(FileUtil.file(filePath));
    12. Admin currentAdmin = TokenUtils.getCurrentAdmin();
    13. String token = TokenUtils.genToken(currentAdmin.getId().toString(), currentAdmin.getPassword(), 15);
    14. String url = "http://localhost:9090/api/book/file/download/" + flag + "?&token=" + token;
    15. if (originalFilename.endsWith("png") || originalFilename.endsWith("jpg") || originalFilename.endsWith("pdf")) {
    16. url += "&play=1";
    17. }
    18. return Result.success(url);
    19. } catch (Exception e) {
    20. log.info("文件上传失败", e);
    21. }
    22. return Result.error("文件上传失败");
    23. }
    24. @GetMapping("/file/download/{flag}")
    25. public void download(@PathVariable String flag, @RequestParam(required = false) String play, HttpServletResponse response) {
    26. OutputStream os;
    27. List<String> fileNames = FileUtil.listFileNames(BASE_FILE_PATH);
    28. String fileName = fileNames.stream().filter(name -> name.contains(flag)).findAny().orElse(""); // System.currentTimeMillis() + originalFilename
    29. try {
    30. if (StrUtil.isNotEmpty(fileName)) {
    31. String realName = fileName.substring(fileName.indexOf("_") + 1);
    32. if ("1".equals(play)) {
    33. response.addHeader("Content-Disposition", "inline;filename=" + URLEncoder.encode(realName, "UTF-8"));
    34. } else {
    35. response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(realName, "UTF-8"));
    36. }
    37. byte[] bytes = FileUtil.readBytes(BASE_FILE_PATH + fileName);
    38. os = response.getOutputStream();
    39. os.write(bytes);
    40. os.flush();
    41. os.close();
    42. }
    43. } catch (Exception e) {
    44. log.error("文件下载失败", e);
    45. }
    46. }
    复制代码

    文件上传接口

    1.接口设置

    • @postMapping(“file/upload”):这个注解表明这个方法处理HTTP POST请求,请求路径是/file/upload。
    • Public Result uploadFile(Multipartfile file):Result是一个自定义的响应对象,包含了操作成功或操作失败的状态,以及相关数据。

    2.文件处理

    • String originalFilename = file.getOriginalFilename():获取上传文件的原始文件名。
    • if (StrUtil.isBlank(originalFilename)):检查文件名是否为空,如果为空,返回错误响应(Result.error)。
    • long flag = System.currentTimeMillis():获取当前时间戳(毫秒),用于创建唯一的文件名。
    • String filePath = BASE_FILE_PATH + flag + "_" + originalFilename:构建完整的文件保存路径。BASE_FILE_PATH指存储文件的根目录,文件名由时间戳和原始文件名拼接而成。

    3.文件保存

    • FileUtil.mkParentDirs(filePath):确保所有父目录都存在,如果不存在,则创建它们。
    • file.transferTo(FileUtil.file(filePath)):这行关键代码将上传的文件内容保存到服务器上的filePath路径。

    4.令牌生成(可选)

    • Admin currentAdmin = TokenUtils.getCurrentAdmin():获取当前登录的管理员用户,这意味着实现了身份验证。
    • String token = TokenUtils.genToken(currentAdmin.getId().toString(), currentAdmin.getPassword(), 15):根据管理员ID、密码和15分钟的过期时间生成一个jwt令牌。

    5.URL构建

    • String url = "http://localhost:9090/api/book/file/download/" + flag + "?&token=" + token:构建一个指向下载接口(/api/book/file/download/)的URL,包含时间戳(flag)和生成的令牌。
    • if(originalFilename.endsWith("png")||originalFilename.endsWith("jpg") || originalFilename.endsWith("pdf")):如果是图片(png、jpg)或pdf文件,则在URL后面添加&play=1,这表示服务器可能提供文件预览功能。

    6.响应

    • return Result.success(url):上传成功后,返回包含下载的Result.success(url)响应
    • catch (Exception e):处理过程中可能出现的异常,记录错误日志并返回一个通用的Result.error响应。

    文件下载接口

    1.注解

    • (1)@GetMapping("/file/download/{flag}"):使用GetingMapping注解处理Get请求,路径包含一个变量{flag},将被捕获并传递给方法。
    • (2)@PathVariable String flag:此注解将{flag}路径变量的值绑定到方法的flag参数。
    • (3)@RequestParam(required = false) String play:此注解将可选查询参数”play”的值绑定到方法的play参数。required=false使此参数成为可选参数。

    2.方法参数:

    • (1)String flag:此参数将包含作为URL中{flag}传递的值。它用于标识要下载的文件。
    • (2)String play:此可选参数(来自查询字符串)可用于控制下载行为。如果其值为“1”,则文件可能会直接在浏览器中打开(内联)。否则,它将被视为常规下载。
    • (3)HttpServletResponse response:此对象用于操作发送回客户端的HTTP响应。

    3.文件处理

    • (1)List fileNames = FileUtil.listFileNames(BASE_FILE_PATH):此行假设存在一个FileUtil类,其中包含一个listFileNames方法,该方法从BASE_FILE_PATH指定的目录中检索文件名列表。
    • (2)String fileName = fileNames.stream().filter(name -> name.contains(flag)).findAny().orElse(""):此行使用Java流查找包含提供的flag的文件名。
    • (3)String realName = fileName.substring(fileName.indexOf("_") + 1):通过删除找到的文件名中的任何前缀(第一个”_”之前)来提取实际文件名。

    响应处理

    1. if ("1".equals(play)) {
    2. response.addHeader("Content-Disposition", "inline;filename=" + URLEncoder.encode(realName, "UTF-8"));
    3. } else {
    4. response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(realName, "UTF-8"));
    5. }
    复制代码

    检查play参数的值。如果play为“1”,则将Content-Disposition标头设置为"inline;filename=" + URLEncoder.encode(realName, "UTF-8"),这通常告诉浏览器尝试直接打开文件。否则,他会将标头设置为 "attachment;filename=" + URLEncoder.encode(realName, "UTF-8"),这通常会强制浏览器下载文件。

    • URLEncoder.encode(realName, "UTF-8")使用UTF-8编码对文件名进行编码,以正确处理文件名中的特殊字符和空格。
    • byte[] bytes = FileUtil.readBytes(BASE_FILE_PATH + fileName);使用FileUtil类中的另一个假设方法将文件内容读入字节数组。
    • os = response.getOutputStream():从response对象获取输出流,允许代码将数据直接写入HTTP响应体。
    • os.write(bytes);os.flush(); os.close();:将文件内容(字节数组)写入输出流,刷新流以确保发送所有数据,然后关闭流。
    • catch (Exception e) { log.error("文件下载失败", e);}:此catch块捕获在文件下载过程中抛出的任何异常,并记录错误消息。

    总结

    以上为个人经验,希望能给大家一个参考,也希望大家多多支持晓枫资讯。


    免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
    晓枫资讯-科技资讯社区-免责声明
    免责声明:以上内容为本网站转自其它媒体,相关信息仅为传递更多信息之目的,不代表本网观点,亦不代表本网站赞同其观点或证实其内容的真实性。
          1、注册用户在本社区发表、转载的任何作品仅代表其个人观点,不代表本社区认同其观点。
          2、管理员及版主有权在不事先通知或不经作者准许的情况下删除其在本社区所发表的文章。
          3、本社区的文章部分内容可能来源于网络,仅供大家学习与参考,如有侵权,举报反馈:点击这里给我发消息进行删除处理。
          4、本社区一切资源不代表本站立场,并不代表本站赞同其观点和对其真实性负责。
          5、以上声明内容的最终解释权归《晓枫资讯-科技资讯社区》所有。
    http://bbs.yzwlo.com 晓枫资讯--游戏IT新闻资讯~~~

      离线 

    TA的专栏

    等级头衔

    等級:晓枫资讯-列兵

    在线时间
    0 小时

    积分成就
    威望
    0
    贡献
    0
    主题
    0
    精华
    0
    金钱
    17
    积分
    14
    注册时间
    2022-12-27
    最后登录
    2022-12-27

    发表于 2025-4-1 02:24:46 | 显示全部楼层
    顶顶更健康!!!
    http://bbs.yzwlo.com 晓枫资讯--游戏IT新闻资讯~~~
    严禁发布广告,淫秽、色情、赌博、暴力、凶杀、恐怖、间谍及其他违反国家法律法规的内容。!晓枫资讯-社区
    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    1楼
    2楼

    手机版|晓枫资讯--科技资讯社区 本站已运行

    CopyRight © 2022-2025 晓枫资讯--科技资讯社区 ( BBS.yzwlo.com ) . All Rights Reserved .

    晓枫资讯--科技资讯社区

    本站内容由用户自主分享和转载自互联网,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。

    如有侵权、违反国家法律政策行为,请联系我们,我们会第一时间及时清除和处理! 举报反馈邮箱:点击这里给我发消息

    Powered by Discuz! X3.5

    快速回复 返回顶部 返回列表