java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Java中的MultipartFile接口和File类

Java中的MultipartFile接口和File类解读

作者:执念1314

本文主要介绍了Java中的File类和Spring框架中的MultipartFile接口,File类提供了对文件和目录操作的方法,如创建、删除、重命名、判断文件是否存在等,MultipartFile接口用于处理文件上传,提供了获取上传文件信息和保存上传文件的方法

一、File类

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

获取文件信息

文件和目录操作

文件路径操作

文件过滤

二、MultipartFile接口

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

它通常用于处理multipart/form-data类型的请求,例如处理文件上传的表单。

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

2.1 源码和方法功能

public interface MultipartFile extends InputStreamSource {
    String getName();
 
    @Nullable
    String getOriginalFilename();
 
    @Nullable
    String getContentType();
 
    boolean isEmpty();
 
    long getSize();
 
    byte[] getBytes() throws IOException;
 
    InputStream getInputStream() throws IOException;
 
    default Resource getResource() {
        return new MultipartFileResource(this);
    }
 
    void transferTo(File dest) throws IOException, IllegalStateException;
 
    default void transferTo(Path dest) throws IOException, IllegalStateException {
        FileCopyUtils.copy(this.getInputStream(), Files.newOutputStream(dest));
    }
}

2.2 void transferTo(File dest)

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

CommonsMultipartFile中的方法体

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

public void transferTo(File dest) throws IOException, IllegalStateException {
        if (!this.isAvailable()) {
            throw new IllegalStateException("File has already been moved - cannot be transferred again");
        } else if (dest.exists() && !dest.delete()) {
            throw new IOException("Destination file [" + dest.getAbsolutePath() + "] already exists and could not be deleted");
        } else {
            try {
                this.fileItem.write(dest);
                LogFormatUtils.traceDebug(logger, (traceOn) -> {
                    String action = "transferred";
                    if (!this.fileItem.isInMemory()) {
                        action = this.isAvailable() ? "copied" : "moved";
                    }
 
                    return "Part '" + this.getName() + "',  filename '" + this.getOriginalFilename() + "'" + (traceOn ? ", stored " + this.getStorageDescription() : "") + ": " + action + " to [" + dest.getAbsolutePath() + "]";
                });
            } catch (FileUploadException var3) {
                throw new IllegalStateException(var3.getMessage(), var3);
            } catch (IOException | IllegalStateException var4) {
                throw var4;
            } catch (Exception var5) {
                throw new IOException("File transfer failed", var5);
            }
        }
    }

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

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

// 获取上传文件的原始文件名
String originalFilename = StringUtils.cleanPath(file.getOriginalFilename());
 
// 构建目标文件对象
File destFile = new File("/path/to/destination/directory", originalFilename);
 
try {
    // 将上传文件保存到目标文件
    file.transferTo(destFile);
    return "File uploaded successfully!";
} catch (IOException e) {
    e.printStackTrace();
    return "Failed to upload the file.";
}

StandardMultipartHttpServletRequest实现类

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

default void transferTo(Path dest)

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

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

// 构建目标文件路径
String uploadDirectory = "/path/to/destination/directory";
String originalFilename = file.getOriginalFilename();
Path filePath = Paths.get(uploadDirectory, originalFilename);
 
try {
// 将上传文件保存到目标文件
    file.transferTo(filePath);
    return "File uploaded successfully!";
} catch (IOException e) {
    e.printStackTrace();
    return "Failed to upload the file.";
}

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

@PostMapping("/file/upload")
    public Result uploadFile(MultipartFile file) {
        String originalFilename = file.getOriginalFilename();
        if (StrUtil.isBlank(originalFilename)) {
            return Result.error("文件上传失败");
        }
        long flag = System.currentTimeMillis();
        String filePath = BASE_FILE_PATH + flag + "_" + originalFilename;
        try {
            FileUtil.mkParentDirs(filePath);  // 创建父级目录
            file.transferTo(FileUtil.file(filePath));
            Admin currentAdmin = TokenUtils.getCurrentAdmin();
            String token = TokenUtils.genToken(currentAdmin.getId().toString(), currentAdmin.getPassword(), 15);
            String url = "http://localhost:9090/api/book/file/download/" + flag + "?&token=" + token;
            if (originalFilename.endsWith("png") || originalFilename.endsWith("jpg") || originalFilename.endsWith("pdf")) {
                url += "&play=1";
            }
            return Result.success(url);
        } catch (Exception e) {
            log.info("文件上传失败", e);
        }
        return Result.error("文件上传失败");
    }

    @GetMapping("/file/download/{flag}")
    public void download(@PathVariable String flag, @RequestParam(required = false) String play, HttpServletResponse response) {
        OutputStream os;
        List<String> fileNames = FileUtil.listFileNames(BASE_FILE_PATH);
        String fileName = fileNames.stream().filter(name -> name.contains(flag)).findAny().orElse(""); //  System.currentTimeMillis() + originalFilename
        try {
            if (StrUtil.isNotEmpty(fileName)) {
                String realName = fileName.substring(fileName.indexOf("_") + 1);
                if ("1".equals(play)) {
                    response.addHeader("Content-Disposition", "inline;filename=" + URLEncoder.encode(realName, "UTF-8"));
                } else {
                    response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(realName, "UTF-8"));
                }
                byte[] bytes = FileUtil.readBytes(BASE_FILE_PATH + fileName);
                os = response.getOutputStream();
                os.write(bytes);
                os.flush();
                os.close();
            }
        } catch (Exception e) {
            log.error("文件下载失败", e);
        }
    }

文件上传接口

1.接口设置

2.文件处理

3.文件保存

4.令牌生成(可选)

5.URL构建

6.响应

文件下载接口

1.注解

2.方法参数:

3.文件处理

响应处理

if ("1".equals(play)) {

                    response.addHeader("Content-Disposition", "inline;filename=" + URLEncoder.encode(realName, "UTF-8"));

                } else {

                    response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(realName, "UTF-8"));

                }

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

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

您可能感兴趣的文章:
阅读全文