3

序言

笔者最近在查看作业评阅系统的时候,对于其中的附件上传和下载不太了解,通过写这篇文章来记录自己的收获。

文件传到哪里

1.传到工程目录下

在一些文件存储量很小的工程中,有一些上传文件放置在工程本身的目录下,但是随着文件上传的量越来越大,工程本身所在的文件夹容量会越来越大,不仅打包和部署的效率会降低,工程的启动和运行也会变慢,所以一般不会采用这做法。

2.上传到工程所在服务器

将文件专门上传到Web应用工程所在容器(如Tomcat)位于的服务器中,单独开辟一个盘符或文件夹用于存储上传的图片,这种做法让上传 文件与工程本身分离,工程的打包和启动效率不受到任何影响。但是如果以后出现了海量图片,Web应用工程所在的服务器的效率会降低,这样也会间接地降低应用的执行效率,所以在上传图片量不大的情况下,可以采用该做法。

3.搭建文件服务器

一般大型的互联网项目,都会为自己的文件上传单独架设一个文件服务器(有集群的应用,可能会有多台文件服务器),也有独立处理文件上传、文件访问的服务器。这种方案就是太烧钱。
总结:上面分析了三种方案的特点和优缺点。第一种一般不采取,第二种可能会采取,最常用的就是第三种方案。笔者所用系统是使用的第一种方案。

上传

MultipartFile工具类

MultipartFileSpringMVC提供简化上传操作的工具类。
在不使用框架之前,都是使用原生的HttpServletRequest来接收上传的数据,文件是以二进制流传递到后端的,然后需要我们自己转换为File类,非常麻烦。使用了MultipartFile工具类之后,我们对文件上传的操作就简便许多了。
以下是MultipartFile工具类全部的接口方法:

方法名返回值作用
getContentType()String在取文件MIME类型
getlnputStream()InputStream获取文件流
getName()String获取 form 表单中文件组件的名字
getOriginalFilename()String获取上传文件件的原名
getSize()long获取文件的大小,单位为byte
isEmpty()boolean是否为空
transferTo(File dest)void将数据保存到一个目标文件中

上传实现:
1.空文件不执行上传操作。
2.给文件按照一定的命名规则进行重命名。此举即可以防止命名冲突,也可以从一定程度上防止病毒木马等入侵,可以采取其它命名策略(如SHA、MD5等),但是要保证命名的合理性和安全性,此处示例代码并没有进行处理。
3.若文件命名策略可能生成同名文件(如两个用户上传了同一个文件),可采用后上传的文件不执行保存操作。示例没有此操作,因为查询文件是否存在方式不同,所以并未展示。

public void saveFile(MultipartFile multipartFile, Path saveFilePath) throws Exception {
    
    logger.debug("获取文件名");
    String fileName = multipartFile.getOriginalFilename();

    logger.debug("从文件名中截取拓展名");
    // 从"."最后一次出现的位置的下一位开始截取,获取扩展名
    assert fileName != null;
    String ext = fileName.substring(fileName.lastIndexOf(".") + 1);

    logger.debug("设置保存文件名");
    saveName = fileName;

    logger.debug("判断上传的文件是否为空");
    if (multipartFile.isEmpty()) {
      throw new RuntimeException("上传的附件不能为空" + fileName);
    }

    logger.debug("如果目录不存在,则创建目录。如果目录存在,则不创建");
    if (!Files.exists(saveFilePath)) {
      Files.createDirectories(saveFilePath);
      new File(saveFilePath.resolve("index.html").toString()).createNewFile();
    }

    logger.debug("将文件移动至储存文件的路径下");
    Files.copy(multipartFile.getInputStream(), saveFilePath.resolve(saveName),
          StandardCopyOption.REPLACE_EXISTING);
}

笔者直接用文件名来将文件存储在工程中,这种行为是很不安全的,所以建议用自己定义的加密解密办法来处理此问题,存入服务器的文件就会变成二进制文件,当别人直接冲服务器拿到文件时,也无法查看,这就保证了安全性。

下载

实现过程:获取文件存储地址->读取文件到输入流->将输入流复制到输出流
下载部分核心代码

public void download(File f, OutputStream outputStream) {
    // File为自定义实体,其中存储了文件位置和存储名等信息
    Path path = Paths.get(f.getPath())
        .resolve(f.getName());
    java.io.File file = path.toFile();
    // 输出文件类型
    FileInputStream inputStream;
    try {
      inputStream = new FileInputStream(file);
    } catch (FileNotFoundException e) {
      logger.error("读取文件出错" + f.getId() + file.getAbsolutePath());
      e.printStackTrace();
      throw new RuntimeException("读取文件发生错误");
    }

    try {
      org.apache.commons.io.IOUtils.copy(inputStream, outputStream);
    } catch (IOException e) {
      logger.error("下发数据时发生了错误");
      e.printStackTrace();
      throw new RuntimeException("下发数据时发生了错误");
    }
  }

由于本周事情较多,没有实现一个demo来更好的介绍,接下来会尽快实现


郝泽龙_HZ
182 声望2 粉丝