SpringBoot-文件上传下载

1、概述

  • 文件上传是项目开发中最常见的功能之一,SpringMVC可以很好的支持文件上传,但是SpringMVC上下文中默认没有装配MultipartResolver,因此默认情况下其不能处理文件上传工作。如果想使用Spring的文件上传功能,则需要在上下文中配置MultipartResolver。
  • 前端表单要求:为了能上传文件,必须将表单的method设置为post,并将enctype设置为multipart/form-data。只有在这样的情况下,浏览器才会把用户选择的文件以二进制数据发送给服务器。

对表单中的enctype属性做个详细的说明:

  • application/x-www=form-urlencoded:默认方式,只处理表单域中的 value 属性值,采用这种编码方式的表单会将表单域中的值处理成 URL 编码方式。
  • multipart/form-data:这种编码方式会以二进制流的方式来处理表单数据,这种编码方式会把文件域指定文件的内容也封装到请求参数中,不会对字符编码。form中设置enctype=”multipart/form-data”时会导致参数绑定失败。
  • text/plain:除了把空格转换为 “+” 号外,其他字符都不做编码处理,这种方式适用直接通过表单发送邮件。
<form action="" enctype="multipart/form-data" method="post">
   <input type="file" name="file"/>
   <input type="submit">
</form>

一旦设置了enctype为multipart/form-data,浏览器即会采用二进制流的方式来处理表单数据,而对于文件上传的处理则涉及在服务器端解析原始的HTTP响应。在2003年,Apache Software Foundation发布了开源的Commons FileUpload组件,其很快成为Servlet/JSP程序员上传文件的最佳选择。

  • Servlet3.0规范已经提供方法来处理文件上传,但这种上传需要在Servlet中完成。
  • 而Spring MVC则提供了更简单的封装。
  • Spring MVC为文件上传提供了直接的支持,这种支持是用即插即用的MultipartResolver实现的。
  • Spring MVC使用Apache Commons FileUpload技术实现了一个MultipartResolver实现类:
  • CommonsMultipartResolver。因此,SpringMVC的文件上传还需要依赖Apache Commons FileUpload的组件

注:前端页面使用 thymeleaf 模板引擎

2、文件上传

  • 创建前端页面
<div class="col-lg-6">
    <section class="panel">
        <header class="panel-heading">
            Basic Forms
        </header>
        <div class="panel-body">
            <form role="form" th:action="@{/upload}" method="post" enctype="multipart/form-data">
                <div class="form-group">
                    <label for="exampleInputEmail1">邮箱</label>
                    <input type="email" name="email" class="form-control" id="exampleInputEmail1" placeholder="Enter email">
                </div>
                <div class="form-group">
                    <label for="exampleInputPassword1">名字</label>
                    <input type="text" name="username" class="form-control" id="exampleInputPassword1" placeholder="username">
                </div>
                <div class="form-group">
                    <label for="exampleInputFile">头像</label>
                    <input type="file" name="headerImg" id="exampleInputFile">
                </div>
                <div class="form-group">
                    <label for="exampleInputFile">生活照</label>
                    <input type="file" name="photos" multiple>
                </div>
                <div class="checkbox">
                    <label>
                        <input type="checkbox"> Check me out
                    </label>
                </div>
                <button type="submit" class="btn btn-primary">Submit</button>
            </form>

        </div>
    </section>
</div>
  • 创建controller
@RequestMapping("/form_layouts")
public String form_layouts() {
    return "form/form_layouts";
}

// 文件上传
@RequestMapping("/upload")
public String upload(HttpServletRequest request,
                     @RequestParam("email") String email,
                     @RequestParam("username") String username,
                     @RequestPart("headerImg") MultipartFile headerImg,
                     @RequestPart("photos") MultipartFile[] photos) throws IOException {
    log.info("信息为,邮箱地址:{},名字:{},headerImg:{},photos:{}",
            email, username, headerImg.getSize(), photos.length);

    // 单文件上传
    if (!headerImg.isEmpty()) {
        // 获取上传文件的原始名称
        String originalFilename = headerImg.getOriginalFilename();
        // 设置上传文件的保存地址目录(存放在项目路径下)
        String dirPath = "xxx";

		// 字符串截取后缀名
        String suffix = uploadFile.getOriginalFilename().substring(uploadFile.getOriginalFilename().lastIndexOf('.'));

        // 设置上传后的文件名称(拼接)
        String newFileName = username + UUID.randomUUID() + "_" + originalFilename;

        headerImg.transferTo(new File(dirPath + "/" + newFileName));
    }

    // 多文件上传
    if (photos.length > 0) {
        for (MultipartFile photo : photos) {
            photo.transferTo(
                    new File("上传文件的保存目录路径"
                            + "/" + username + UUID.randomUUID() + "_" + photo.getOriginalFilename()));
        }
    }
    return "form/form_layouts";
}
  • 修改yaml配置文件,设置上传最大值
# 文件上传最大值
spring:
  servlet:
    multipart:
      max-file-size: 10MB
      max-request-size: 100MB

3、文件下载

  • 导入依赖
<!--文件下载-->
<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.6</version>
</dependency>
  • 创建下载页面
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">

<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

    <a th:href="@{/download(filename=1.jpg)}">英文文件下载</a><br>
    <!-- URLEncoder.encode:使用Encoder类的encoder()方法对中文名进行编码 -->
    <a th:href="@{/download(filename=小koen同学10.jpg)}">中文文件下载</a>

    🐷
    <form th:action="@{/download1}" method="post">
        <input type="file" name="filename"/><br>
        <input type="submit">
    </form>

</body>
</html>
  • 创建controller
// 跳转到download.html中
@RequestMapping("/toDownload")
public String toDownload(){
    return "form/download";
}

// 文件下载
@RequestMapping("/download")
public ResponseEntity<byte[]> fileDownload(HttpServletRequest request, String filename) throws Exception {
    // 指定要下载的文件所在策路径
    String dirPath = "F:\\idea_workspace\\FrameWork\\springboot-file";

    // 创建该文件对象
    File file = new File(dirPath + File.separator + filename);
    System.out.println(dirPath + File.separator + filename);
    System.out.println("分隔符为:" + File.pathSeparator);

    // 对文件名编码,防止中文乱码
    filename = this.getFileName(request, filename);

    // 设置响应头
    HttpHeaders headers = new HttpHeaders();
    // 通知浏览器以下载的方式打开文件
    headers.setContentDispositionFormData("attachment", filename);

    // 定义以流的方式下载返回文件的数据(stream:流)
    headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
    // 将该对象读取时按字节传送
    byte[] array = FileUtils.readFileToByteArray(file);
    // 使用SpringMVC框架的ResponseEntity对象封装返回下载数据
    ResponseEntity<byte[]> responseEntity = new ResponseEntity<>(array, headers, HttpStatus.OK);
    return responseEntity;
}

/**
 * 根据浏览器的不同进行编码设置,返回编码后的文件名
 */
public String getFileName(HttpServletRequest request, String filename) throws Exception {
    //IE不同版本User-Agent中出现的各种关键词
    String[] IEBrowserKeyWords = {"MSIE", "Trident", "Edge"};
    //获取请求头代理信息
    String userAgent = request.getHeader("User-Agent");
    for (String keyWord : IEBrowserKeyWords) {
        if (userAgent.contains(keyWord)) {
            //IE内核浏览器,统一为UTF-8编码显示
            return URLEncoder.encode(filename, "UTF-8");
        }
    }
    //其它浏览器统一为ISO-8859-1编码显示
    return new String(filename.getBytes("UTF-8"), "ISO-8859-1");
}

@RequestMapping("/download1")
public ResponseEntity<byte[]> download1(String filename, HttpServletRequest request) throws Exception {

    // 确定要下载的文件路径
    String dirPath = "F:\\idea_workspace\\FrameWork\\springboot-file";

    // 创建该文件对象(谷歌所用)
    File file = new File(dirPath + "/" + filename);
//IE、百度
//    File file = new File(filename);
    System.out.println(file);

    //对文件名编码,防止中文乱码
    filename = this.getFileName(request,filename);

    // 设置响应头
    HttpHeaders headers = new HttpHeaders();
    // 通知浏览器以下载的方式打开文件
    headers.setContentDispositionFormData("attachment", filename);
    // 定义以流的方式下载返回文件的数据
    headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
    // 将该对象读取时按字节传送
    byte[] array = FileUtils.readFileToByteArray(file);
    // 使用SpringMVC框架的ResponseEntity对象封装返回下载数据
    ResponseEntity<byte[]> responseEntity = new ResponseEntity<>(array, headers, HttpStatus.OK);
    return responseEntity;
}
  • 解决中文乱码问题
 /**
     * 根据浏览器的不同进行编码设置,返回编码后的文件名
     */
public String getFileName(HttpServletRequest request, String filename) throws Exception {
    //IE不同版本User-Agent中出现的各种关键词
    String[] IEBrowserKeyWords = {"MSIE", "Trident", "Edge"};
    //获取请求头代理信息
    String userAgent = request.getHeader("User-Agent");
    for (String keyWord : IEBrowserKeyWords) {
        if (userAgent.contains(keyWord)) {
            //IE内核浏览器,统一为UTF-8编码显示
            return URLEncoder.encode(filename, "UTF-8");
        }
    }
    //其它浏览器统一为ISO-8859-1编码显示
    return new String(filename.getBytes("UTF-8"), "ISO-8859-1");
}

4、将文件存储到云服务器

  • 阿里云对象存储OSS(Object Storage Service),是一款海量、安全、低成本、高可靠的云存储服务。
  • 使用OSS,您可以通过网络随时存储和调用包括文本、图片、音频和视频等在内的各种文件。
  • 在我们使用了阿里云OSS对象存储服务之后,我们的项目当中如果涉及到文件上传这样的业务,在前端进行文件上传并请求到服务端时,在服务器本地磁盘当中就不需要再来存储文件了。我们直接将接收到的文件上传到oss,由 oss帮我们存储和管理,同时阿里云的oss存储服务还保障了我们所存储内容的安全可靠。
  • 如何在项目当中来使用云服务完成具体的业务功能?

使用第三方服务步骤

在这里插入图片描述
★★★非必须充值,我们在这里只是暂用(每个用户阿里云都会给一些流量)

Bucket:存储空间是用户用于存储对象(Object,就是文件)的容器,所有的对象都必须隶属于某个存储空间。

SDK:Software Development Kit 的缩写,软件开发工具包,包括辅助软件开发的依赖(jar包)、代码示例等,都可以叫做SDK。

简单说,sdk中包含了我们使用第三方云服务时所需要的依赖,以及一些示例代码。我们可以参照sdk所提供的示例代码就可以完成入门程序。

1. 注册阿里云

在这里插入图片描述

2. 开通对象存储服务(OSS)

搜索找到OSS对象存储服务
在这里插入图片描述
第一次访问,需要开通对象存储服务
在这里插入图片描述

3. 开通OSS服务之后,就可以进入到阿里云对象存储的控制台,创建Bucket

在这里插入图片描述
在这里插入图片描述

4. 创建成功

在这里插入图片描述

5. 获取AccessKey(秘钥)

  • 点进去之后会有一个提示,建议我们创建子用户,我们主账号等价于root,有所有权限,防止泄露

在这里插入图片描述
创建子用户
在这里插入图片描述
在这里插入图片描述

5.1 给新建的用户给权限

在这里插入图片描述
在这里插入图片描述

6. 地域节点(后面用)

在这里插入图片描述

SDK参考文档

  • 阿里云oss 对象存储服务的准备工作我们已经完成了,接下来我们就来完成第二步操作:参照官方所提供的sdk示例来编写入门程序。
  • 首先我们需要来打开阿里云OSS的官方文档,在官方文档中找到 SDK 的示例代码:
    在这里插入图片描述
    在这里插入图片描述

阿里云OSS集成(详见SDK参考文档)

在这里插入图片描述

1、方式一

  1. 引入依赖
<dependency>
     <groupId>com.aliyun.oss</groupId>
     <artifactId>aliyun-sdk-oss</artifactId>
     <version>3.16.1</version>
</dependency>
  1. 图片上传工具类
public class AliOSSUtils {

    // Endpoint以华东1(杭州)为例,其它Region请按实际情况填写。
    private static String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
    // 阿里云账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM用户进行API访问或日常运维,请登录RAM控制台创建RAM用户。
    private static String accessKeyId = "yourAccessKeyId";
    private static String accessKeySecret = "yourAccessKeySecret";
    // 填写Bucket名称,例如examplebucket。
    private static String bucketName = "examplebucket";

    /**
     * 实现上传图片到OSS
     */
    public static String upload(MultipartFile file) throws IOException {
        // 获取上传的文件的输入流
        InputStream inputStream = file.getInputStream();

        // 避免文件覆盖
        String originalFilename = file.getOriginalFilename();
        String fileName = UUID.randomUUID().toString() + originalFilename.substring(originalFilename.lastIndexOf("."));

        //上传文件到 OSS
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
        ossClient.putObject(bucketName, fileName, inputStream);

        //文件访问路径
        String url = endpoint.split("//")[0] + "//" + bucketName + "." + endpoint.split("//")[1] + "/" + fileName;
        // 关闭ossClient
        ossClient.shutdown();
        // 把上传到oss的路径返回
        return url;
    }
}
  1. Controller
@PostMapping("/upload")
public Result updateFile(MultipartFile image) throws IOException {
    String resultUrl = aliOSSUtils.upload(image);
    return Result.success(resultUrl);
}

2、方式二

  • 创建工具类
@Data
@Getter
@Setter
public class AliOSSUtils {

    private String endpoint;
    private String accessKeyId;
    private String accessKeySecret;
    private String bucketName;

    /**
     * 实现上传图片到OSS
     */
    public String upload(MultipartFile file) throws IOException {
        // 获取上传的文件的输入流
        InputStream inputStream = file.getInputStream();

        // 避免文件覆盖
        String originalFilename = file.getOriginalFilename();
        String fileName = UUID.randomUUID().toString() + originalFilename.substring(originalFilename.lastIndexOf("."));

        //上传文件到 OSS
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
        ossClient.putObject(bucketName, fileName, inputStream);

        //文件访问路径
        String url = endpoint.split("//")[0] + "//" + bucketName + "." + endpoint.split("//")[1] + "/" + fileName;
        // 关闭ossClient
        ossClient.shutdown();
        // 把上传到oss的路径返回
        return url;
    }
}
  • 自定义配置类
<!--自定义配置类-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
@Configuration
public class AliOSSConfig {
    @Bean
    @ConfigurationProperties(prefix = "koen-aliyun")
    public AliOSSUtils aliOSSUtils(){
        return new AliOSSUtils();
    }
}
  • 配置yaml
koen-aliyun:
  endpoint: https://oss-cn-beijing.aliyuncs.com
  accessKeyId: yourAccessKeyId
  accessKeySecret: yourAccessKeySecret
  bucketName: delivery-sys-bucket
  • Controller直接调用工具类
@PostMapping("/upload")
public Result upload(MultipartFile imgFile) throws IOException {
    String resultUrl = aliOSSUtils.upload(imgFile);
    return new Result(true, MessageConst.UPLOAD_SUCCESS, resultUrl);
}

5、向本地存储

  • yaml
delivery:
  path: D:\..Amajor\data\img
  • R
@Data
public class R<T> {

    /**
     * 编码:1成功,0和其它数字为失败
     */
    private Integer code;

    /**
     * 错误信息
     */
    private String msg;

    /**
     * 数据
     */
    private T data;

    /**
     * 动态数据
     */
    private Map map = new HashMap();

    public static <T> R<T> success(T object) {
        R<T> r = new R<T>();
        r.data = object;
        r.code = 1;
        return r;
    }

    public static <T> R<T> error(String msg) {
        R<T> r = new R<T>();
        r.msg = msg;
        r.code = 0;
        return r;
    }

    public R<T> add(String key, Object value) {
        this.map.put(key, value);
        return this;
    }
}
  • 文件上传
@Slf4j
@RestController
@RequestMapping("/common")
public class CommonController {
    //使用配置文件中的属性
    //1、Yml文件配置上传路径
    @Value("${delivery.path}")
    private String basePath;


    /**
     * 文件上传
     * @param file
     * @return
     */
    @PostMapping("/upload")
    //前端文件上传我们接收到 接收文件MultipartFile
    public R<String> upload(MultipartFile file){
        // 1.yml文件配置上传路径
        // 2. 获取文件名和后缀名
        String filename = file.getOriginalFilename();
        // lastIndexOf:返回字符串最后出现 . 的位置
        int index = filename.lastIndexOf(".");
        // 获取后缀名
        String suffix = filename.substring(index);
        // 3. 使用UUID重新生成新文件名
        UUID randomUUID = UUID.randomUUID();
        String newFileName = randomUUID + suffix;
        // 4. 创建图片保存的基本目录
        File dir = new File(basePath);
        if (!dir.exists()) {
            // 创建目录
            dir.mkdirs();
        }
        // 5. 保存临时文件到图片目录
        try {
            file.transferTo(new File(basePath, newFileName));
        } catch (IOException e) {
            e.printStackTrace();
        }
        return R.success(newFileName);
    }    
}
  • 文件下载
/**
* 文件下载
 * @param name
 * @param response
 */
@GetMapping("/download")
public void dowload(@RequestParam String name, HttpServletResponse response){
	try {
        File file = new File(basePath, name);
        // 1. 定义输入流,通过字节输入流读取文件内容
        FileInputStream fileInputStream = new FileInputStream(file);
        // 2. 通过response对象设置响应数据格式(image/jpeg)
        response.setContentType("image/jpeg");
        // 3. 通过response对象,获取到字节输出流
        ServletOutputStream outputStream = response.getOutputStream();
        // 4. 通过输入流读取文件数据,然后通过输出流写回浏览器
        int len = 0;
        // 一次读 1024 = 1kb
        byte[] bytes = new byte[1024];
        while ((len = fileInputStream.read(bytes)) != -1) {
            // 通过输入流读取数据,然后通过输出流写回数据
            outputStream.write(bytes, 0, len);
            outputStream.flush();
        }
        // 5. 关闭资源
        fileInputStream.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_koen

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值