第十四节 SpringBoot上传文件

一、使用上传技术

        (1)在yml中添加上传配置。限制单个文件上传以及多个文件总大小限制。如果超出限制,页面将出现报错页面。

spring:
  #上传文件使用
  servlet:
    multipart:
      #单个文件最大上传大小
      max-file-size: 10MB
      #每次请求上传文件大小最大值
      max-request-size: 30MB

        (2)添加自定义参数。实际项目中一般部署在Linux,需要指明路径。我这里配置的是Windows的路径。

#自定义参数
define:
  nginx:
     path: D:\uploadFile\

        (3)编写一个Controller,用于跳转与上传。指明跳转到templates文件夹下的upload.html页面。

    @GetMapping(value = "/goToUpload")
    public String goToUploadHtml() {
        LOGGER.info("Go To upload.html");
        return "upload";
    }

        (4)编写upload.html页面。页面上传路径为upload与 uploadFiles,用于 单个上传与批量上传。

<!DOCTYPE html>
<html>
<head>
    <title>SpringBoot上传文件</title>
    <meta name="keywords" content="keyword1,keyword2,keyword3"/>
    <meta name="description" content="this is my page"/>
    <meta name="content-type" content="text/html; charset=UTF-8"/>
</head>

<body>
<form enctype="multipart/form-data" method="post" action="upload">
    图片:<input type="file" name="file"/>
    <input type="submit" value="上传"/>
</form>
<br>
<br>
<br>
<br>
<br>
<form enctype="multipart/form-data" method="post" action="uploadFiles">
    图片:<input type="file" name="file"/><br>
    图片:<input type="file" name="file"/><br>
    图片:<input type="file" name="file"/><br>
    <input type="submit" value="上传全部"/>
</form>
</body>
</html>

        (5)在Controller中编写处理逻辑。考虑到多文件上传失败的情况,因此需要把上传,使用事务进行管理。

package com.zhoutianyu.learnspringboot.upload;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;

@Controller
public class UploadController {

    private static final Logger LOGGER = LoggerFactory.getLogger(UploadController.class);
    private static final String NULL_FILE = "";

    @Value("${define.nginx.path}")
    private String nginxPath;

    @Autowired
    private FileService fileService;

    @GetMapping(value = "/goToUpload")
    public String goToUploadHtml() {
        LOGGER.info("Go To upload.html");
        return "upload";
    }


    @PostMapping("/upload")
    @ResponseBody
    public String singleFileUpload(@RequestParam("file") MultipartFile file) {

        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("fileName = {}", file.getOriginalFilename());
        }
        try {
            if (file == null || NULL_FILE.equals(file.getOriginalFilename())) {
                return "upload failure";
            }
            fileService.saveFile(file.getBytes(), nginxPath, file.getOriginalFilename());
        } catch (Exception e) {
            return "upload failure";
        }
        return "upload success";
    }


    @PostMapping("/uploadFiles")
    @ResponseBody
    public String multiFileUpload(@RequestParam("file") MultipartFile[] files) {

        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("fileName = {}", files[0].getOriginalFilename());
        }
        try {

            for (int i = 0; i < files.length; i++) {
                //check file
                if (NULL_FILE.equals(files[i].getOriginalFilename())) {
                    continue;
                }
                fileService.saveFile(files[i].getBytes(), nginxPath, files[i].getOriginalFilename());
            }
        } catch (Exception e) {
            return "upload failure";
        }
        return "upload success";
    }

}
package com.zhoutianyu.learnspringboot.upload;

public interface FileService {

    void saveFile(byte[] file, String filePath, String fileName) throws Exception;
}
package com.zhoutianyu.learnspringboot.upload;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.File;
import java.io.FileOutputStream;

@Service
@Transactional
public class FileServiceImpl implements FileService {

    @Override
    public void saveFile(byte[] file, String filePath, String fileName) throws Exception {
        File targetFile = new File(filePath);
        if (!targetFile.exists()) {
            targetFile.mkdirs();
        }
        FileOutputStream out = new FileOutputStream(filePath + fileName);
        out.write(file);
        out.flush();
        out.close();
    }
}

二、测试

         分别测试:

         (1)单个文件上传

         (2)多文件上传

         (3)上传单文件超过10M(配置文件中要求不能上传超过10M文件)。期望上传失败。

 三、下载文件

        下载文件的代码就更加简单了。

   拨云见日

   在分布式集群环境中,传统的上传文件都是存储在项目的某个文件夹下,比如WEB-INF下面,不对用户暴露。但是在分布式部署的情况下,A服务器上传的文件只能上传到A服务器下载,如果请求被负载均衡转发到B服务器,那么B服务器就下载不到文件。我在实际的工作中解决方案有三种。

   ① 由于分布式系统共享同一个数据库服务器。那么就可以把文件服务器搭建在与数据库同一台机器上。下面的代码中,nginxPath变量就是文件服务器的用于存放文件的文件夹路径。适合私有云与公有云部署。

   ② 把上传的文件直接存放到数据库中,但是这种解决方案被研发经理批评,原因是在数据库迁移会相当麻烦,而且如果有大文件的上传与下载,会占用过多的数据库资源,阻塞整个系统。此方案强烈反对在实际工作中使用,虽然曾经用过。

   ③  购买云文件服务器。例如OSS文件服务器系统,把所有文件上传到公共得文件服务器中,这样就能让多个节点共享资源。缺点也有,如果客户的服务器不能支持访问公网的话,那么此方案会行不通,需要提前与客户确定运行环境。

 @Override
    public void download(HttpServletResponse response, String filename) {
        try {
            // 文件地址,真实环境是存放在文件服务器的
            File file = new File(nginxPath + filename);
            filename = new String((filename).getBytes(), UTF_8);
            // 输入对象
            FileInputStream fis = new FileInputStream(file);
            // 设置相关格式
            response.setContentType("application/force-download");
            response.setCharacterEncoding(UTF_8);
            // 设置下载后的文件名以及header
            response.addHeader("Content-disposition",
                         "attachment;fileName=" + URLEncoder.encode(filename, UTF_8));
            // 创建输出对象
            OutputStream os = response.getOutputStream();
            // 常规操作
            byte[] buf = new byte[1024];
            int len = 0;
            while ((len = fis.read(buf)) != -1) {
                os.write(buf, 0, len);
            }
            fis.close();
        } catch (Exception e) {
            throw new RuntimeException("下载异常异常", e.fillInStackTrace());
        }
    }

四、源码下载

        本章节项目源码:点我下载源代码

        目录贴:跟着大宇学SpringBoot-------目录帖

 

 

posted @ 2022-07-17 12:14  小大宇  阅读(2922)  评论(0编辑  收藏  举报