【笑小枫的SpringBoot系列】【十七】SpringBoot文件上传下载

简介: 【笑小枫的SpringBoot系列】【十七】SpringBoot文件上传下载

本文简介🎨


在java开发中文件的上传、下载、删除功能肯定是很常见的,本文主要基于上传图片或文件到指定的位置展开,通过详细的代码和工具类,讲述java如何实现文件的上传、下载、删除。

后续我会集成更多的云存储空间上传,像阿里云、腾讯云、华为云、又拍云等等,本文后续会把这些相关的上传操作以链接的形式集成到本文中,到家可以先点个关注呦。

本文设计到的所有代码,均在文末的github开源项目里,感兴趣的朋友可以帮忙点个star😘😘😘


功能实现🧐


话不多说,直接开整


首先,需要在yml配置文件里面配置文件上传的路径,这里设想的是图片一个目录,文件一个目录,主要是为了演示,大家可以根据自己的需求进行配置。

file:
  local:
    maxFileSize: 10485760
    imageFilePath: D:/test/image/
    docFilePath: D:/test/file/

接下来,让我们一起看一下功能实现的核心代码。

这里只是为了展示功能实现,一些非相关的代码没有贴,可以在文末github源码里面查看更多。

这里是配置文件的一个配置类,这要为了方便使用yml配置文件里面的配置。


package com.maple.demo.config.bean;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
/**
 * @author 笑小枫
 * @date 2022/7/22
 * @see <a href="https://www.xiaoxiaofeng.com">https://www.xiaoxiaofeng.com</a>
 */
@Data
@Configuration
public class FileProperties {
    // ---------------本地文件配置 start------------------
    /**
     * 图片存储路径
     */
    @Value("${file.local.imageFilePath}")
    private String imageFilePath;
    /**
     * 文档存储路径
     */
    @Value("${file.local.docFilePath}")
    private String docFilePath;
    /**
     * 文件限制大小
     */
    @Value("${file.local.maxFileSize}")
    private long maxFileSize;
    // --------------本地文件配置 end-------------------
}

定义WebMvcConfig实现WebMvcConfigurer,指向我们文件的存放路径。

package com.maple.demo.config;
import com.maple.demo.config.bean.FileProperties;
import lombok.AllArgsConstructor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
 * @author 笑小枫
 * @date 2022/7/22
 * @see <a href="https://www.xiaoxiaofeng.com">https://www.xiaoxiaofeng.com</a>
 */
@Configuration
@AllArgsConstructor
public class WebMvcConfig implements WebMvcConfigurer {
    private final FileProperties fileProperties;
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        //重写方法
        //修改tomcat 虚拟映射
        //定义图片存放路径 这里加/example是为了测试避开系统的token校验,实际访问地址根据自己需求来
        registry.addResourceHandler("/example/images/**").
                addResourceLocations("file:" + fileProperties.getImageFilePath());
        //定义文档存放路径
        registry.addResourceHandler("/example/doc/**").
                addResourceLocations("file:" + fileProperties.getDocFilePath());
    }
}


文件上传下载的核心工具类😉,想白嫖的,copy它就对了…

package com.maple.demo.util.file;
import com.maple.demo.config.bean.ErrorCode;
import com.maple.demo.config.bean.FileProperties;
import com.maple.demo.config.exception.MapleCheckException;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.text.SimpleDateFormat;
import java.util.*;
/**
 * @author 笑小枫
 * @date 2022/7/22
 * @see <a href="https://www.xiaoxiaofeng.com">https://www.xiaoxiaofeng.com</a>
 */
@Slf4j
@Component
@AllArgsConstructor
public class FileUtil {
    private final FileProperties fileProperties;
    private static final List<String> FILE_TYPE_LIST_IMAGE = Arrays.asList(
            "image/png",
            "image/jpg",
            "image/jpeg",
            "image/bmp");
    public String uploadImage(MultipartFile file) {
        // 检查图片类型
        String contentType = file.getContentType();
        if (!FILE_TYPE_LIST_IMAGE.contains(contentType)) {
            throw new MapleCheckException(ErrorCode.COMMON_ERROR, "上传失败,不允许的文件类型");
        }
        int size = (int) file.getSize();
        if (size > fileProperties.getMaxFileSize()) {
            throw new MapleCheckException(ErrorCode.COMMON_ERROR, "文件过大");
        }
        String fileName = file.getOriginalFilename();
        //获取文件后缀
        String afterName = StringUtils.substringAfterLast(fileName, ".");
        //获取文件前缀
        String prefName = StringUtils.substringBeforeLast(fileName, ".");
        //获取一个时间毫秒值作为文件名
        fileName = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()) + "_" + prefName + "." + afterName;
        File filePath = new File(fileProperties.getImageFilePath(), fileName);
        //判断文件是否已经存在
        if (filePath.exists()) {
            throw new MapleCheckException(ErrorCode.COMMON_ERROR, "文件已经存在");
        }
        //判断文件父目录是否存在
        if (!filePath.getParentFile().exists()) {
            filePath.getParentFile().mkdirs();
        }
        try {
            file.transferTo(filePath);
        } catch (IOException e) {
            log.error("图片上传失败", e);
            throw new MapleCheckException(ErrorCode.COMMON_ERROR, "图片上传失败");
        }
        return fileName;
    }
    public List<Map<String, Object>> uploadFiles(MultipartFile[] files) {
        int size = 0;
        for (MultipartFile file : files) {
            size = (int) file.getSize() + size;
        }
        if (size > fileProperties.getMaxFileSize()) {
            throw new MapleCheckException(ErrorCode.COMMON_ERROR, "文件过大");
        }
        List<Map<String, Object>> fileInfoList = new ArrayList<>();
        for (int i = 0; i < files.length; i++) {
            Map<String, Object> map = new HashMap<>();
            String fileName = files[i].getOriginalFilename();
            //获取文件后缀
            String afterName = StringUtils.substringAfterLast(fileName, ".");
            //获取文件前缀
            String prefName = StringUtils.substringBeforeLast(fileName, ".");
            String fileServiceName = new SimpleDateFormat("yyyyMMddHHmmss")
                    .format(new Date()) + i + "_" + prefName + "." + afterName;
            File filePath = new File(fileProperties.getDocFilePath(), fileServiceName);
            // 判断文件父目录是否存在
            if (!filePath.getParentFile().exists()) {
                filePath.getParentFile().mkdirs();
            }
            try {
                files[i].transferTo(filePath);
            } catch (IOException e) {
                log.error("文件上传失败", e);
                throw new MapleCheckException(ErrorCode.COMMON_ERROR, "文件上传失败");
            }
            map.put("fileName", fileName);
            map.put("filePath", filePath);
            map.put("fileServiceName", fileServiceName);
            fileInfoList.add(map);
        }
        return fileInfoList;
    }
    /**
     * 批量删除文件
     *
     * @param fileNameArr 服务端保存的文件的名数组
     */
    public void deleteFile(String[] fileNameArr) {
        for (String fileName : fileNameArr) {
            String filePath = fileProperties.getDocFilePath() + fileName;
            File file = new File(filePath);
            if (file.exists()) {
                try {
                    Files.delete(file.toPath());
                } catch (IOException e) {
                    e.printStackTrace();
                    log.warn("文件删除失败", e);
                }
            } else {
                log.warn("文件: {} 删除失败,该文件不存在", fileName);
            }
        }
    }
    /**
     * 下载文件
     */
    public void downLoadFile(HttpServletResponse response, String fileName) throws UnsupportedEncodingException {
        String encodeFileName = URLDecoder.decode(fileName, "UTF-8");
        File file = new File(fileProperties.getDocFilePath() + encodeFileName);
        // 下载文件
        if (!file.exists()) {
            throw new MapleCheckException(ErrorCode.COMMON_ERROR, "文件不存在!");
        }
        try (FileInputStream inputStream = new FileInputStream(file);
             ServletOutputStream outputStream = response.getOutputStream()) {
            response.reset();
            //设置响应类型  PDF文件为"application/pdf",WORD文件为:"application/msword", EXCEL文件为:"application/vnd.ms-excel"。
            response.setContentType("application/octet-stream;charset=utf-8");
            //设置响应的文件名称,并转换成中文编码
            String afterName = StringUtils.substringAfterLast(fileName, "_");
            //保存的文件名,必须和页面编码一致,否则乱码
            afterName = response.encodeURL(new String(afterName.getBytes(), StandardCharsets.ISO_8859_1.displayName()));
            response.setHeader("Content-type", "application-download");
            //attachment作为附件下载;inline客户端机器有安装匹配程序,则直接打开;注意改变配置,清除缓存,否则可能不能看到效果
            response.addHeader("Content-Disposition", "attachment;filename=" + afterName);
            response.addHeader("filename", afterName);
            //将文件读入响应流
            int length = 1024;
            byte[] buf = new byte[1024];
            int readLength = inputStream.read(buf, 0, length);
            while (readLength != -1) {
                outputStream.write(buf, 0, readLength);
                readLength = inputStream.read(buf, 0, length);
            }
            outputStream.flush();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}


编写一个测试的controller吧

package com.maple.demo.controller;
import com.maple.demo.config.bean.ErrorCode;
import com.maple.demo.config.exception.MapleCheckException;
import com.maple.demo.util.file.FileUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
import java.util.Map;
/**
 * @author 笑小枫
 * @date 2022/7/22
 * @see <a href="https://www.xiaoxiaofeng.com">https://www.xiaoxiaofeng.com</a>
 */
@Slf4j
@RestController
@AllArgsConstructor
@Api(tags = "文件相关操作接口")
@RequestMapping("/example")
public class TestFileController {
    private final FileUtil fileUtil;
    @PostMapping("/uploadImage")
    @ApiOperation("图片上传")
    public String uploadImage(@RequestParam(value = "file") MultipartFile file) {
        if (file.isEmpty()) {
            throw new MapleCheckException(ErrorCode.COMMON_ERROR, "图片内容为空,上传失败!");
        }
        return fileUtil.uploadImage(file);
    }
    @PostMapping("/uploadFiles")
    @ApiOperation("文件批量上传")
    public List<Map<String, Object>> uploadFiles(@RequestParam(value = "file") MultipartFile[] files) {
        return fileUtil.uploadFiles(files);
    }
    @PostMapping("/deleteFiles")
    @ApiOperation("批量删除文件")
    public void deleteFiles(@RequestParam(value = "files") String[] files) {
        fileUtil.deleteFile(files);
    }
    @GetMapping(value = "/download/{fileName:.*}")
    @ApiOperation("文件下载功能")
    public void download(@PathVariable("fileName") String fileName, HttpServletResponse response) {
        try {
            fileUtil.downLoadFile(response, fileName);
        } catch (Exception e) {
            log.error("文件下载失败", e);
        }
    }
}


功能测试✌

单张图片上传

使用postman请求接口:http://localhost:6666/example/uploadImage


6d0821d12b00eb7a23e49e5feb257c4f.png


上传成功后可以在对应的文件夹下面看到我们上传的文件,如下图所示:


53ae9b32b0079715ef2936fa415020b2.png



访问上传的图片


上个case我们上传了一张图片,我们应该怎么进行访问呢?这个我们在WebMvcConfig文件中配置的,地址在yml文件中配置。


发送请求:http://localhost:6666/example/images/20220916153101_29170841.jpg


94e2fbd30cac1000571ddc89526923bd.png


多文件上传


file支持数组形式的上传,测试上传链接:http://localhost:6666/example/uploadFiles

1e2845508387a24a2433a49c1e940cf0.png


删除文件


测试删除文件链接,后面跟文件名即可:http://localhost:6666/example/deleteFiles?files=202209161535210_9c59247e8182b769f7ca23e4ad06b41.jpg

5feff59f82a4c5b0bd3f378c6b879839.png



删除成功后,可以看到我们多文件上传的两张图片仅剩一张了。

a6b1ba9ed54012e70d24f18f25f3cba0.png


云空间文件上传


《JAVA实现上传文件到又拍云》


《JAVA使用阿里云OSS云存储》


关于笑小枫💕


本章到这里结束了,喜欢的朋友关注一下我呦,大伙的支持,就是我坚持写下去的动力。

笑小枫个人博客:https://www.xiaoxiaofeng.com

本文源码:https://github.com/hack-feng/maple-demo

目录
相关文章
|
5月前
|
存储 前端开发 Java
SpringBoot使用云端资源url下载文件的接口写法
在Spring Boot中实现从云端资源URL下载文件的功能可通过定义REST接口完成。示例代码展示了一个`FileDownloadController`,它包含使用`@GetMapping`注解的方法`downloadFile`,此方法接收URL参数,利用`RestTemplate`下载文件,并将文件字节数组封装为`ByteArrayResource`返回给客户端。此外,通过设置HTTP响应头,确保文件以附件形式下载。这种方法适用于从AWS S3或Google Cloud Storage等云服务下载文件。
531 7
|
1月前
|
前端开发 Java easyexcel
SpringBoot操作Excel实现单文件上传、多文件上传、下载、读取内容等功能
SpringBoot操作Excel实现单文件上传、多文件上传、下载、读取内容等功能
125 8
|
2月前
|
easyexcel Java UED
SpringBoot中大量数据导出方案:使用EasyExcel并行导出多个excel文件并压缩zip后下载
在SpringBoot环境中,为了优化大量数据的Excel导出体验,可采用异步方式处理。具体做法是将数据拆分后利用`CompletableFuture`与`ThreadPoolTaskExecutor`并行导出,并使用EasyExcel生成多个Excel文件,最终将其压缩成ZIP文件供下载。此方案提升了导出效率,改善了用户体验。代码示例展示了如何实现这一过程,包括多线程处理、模板导出及资源清理等关键步骤。
|
7月前
|
XML JavaScript 前端开发
springboot配合Freemark模板生成word,前台vue接收并下载【步骤详解并奉上源码】
springboot配合Freemark模板生成word,前台vue接收并下载【步骤详解并奉上源码】
285 2
|
3月前
|
前端开发 Java easyexcel
SpringBoot操作Excel实现单文件上传、多文件上传、下载、读取内容等功能
SpringBoot操作Excel实现单文件上传、多文件上传、下载、读取内容等功能
56 6
|
3月前
|
存储 前端开发 Java
springboot文件上传和下载接口的简单思路
本文介绍了在Spring Boot中实现文件上传和下载接口的简单思路。文件上传通过`MultipartFile`对象获取前端传递的文件并存储,返回对外访问路径;文件下载通过文件的uuid名称读取文件,并通过流的方式输出,实现文件下载功能。
springboot文件上传和下载接口的简单思路
|
2月前
|
JavaScript 前端开发 Java
Springboot+vue实现文件的下载和上传
这篇文章介绍了如何在Springboot和Vue中实现文件的上传和下载功能,包括后端控制器的创建、前端Vue组件的实现以及所需的依赖配置。
299 0
|
4月前
|
Java
Java SpringBoot FTP 上传下载文件
Java SpringBoot FTP 上传下载文件
181 0
|
4月前
|
JavaScript Java
SpringBoot 下载文件
SpringBoot 下载文件
42 0
|
4月前
|
JavaScript Java Spring
SpringBoot 接口输出文件流 & Vue 下载文件流,获取 Header 中的文件名
SpringBoot 接口输出文件流 & Vue 下载文件流,获取 Header 中的文件名
269 0