本文简介🎨
在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
上传成功后可以在对应的文件夹下面看到我们上传的文件,如下图所示:
访问上传的图片
上个case我们上传了一张图片,我们应该怎么进行访问呢?这个我们在WebMvcConfig文件中配置的,地址在yml文件中配置。
发送请求:http://localhost:6666/example/images/20220916153101_29170841.jpg
多文件上传
file支持数组形式的上传,测试上传链接:http://localhost:6666/example/uploadFiles
删除文件
测试删除文件链接,后面跟文件名即可:http://localhost:6666/example/deleteFiles?files=202209161535210_9c59247e8182b769f7ca23e4ad06b41.jpg
删除成功后,可以看到我们多文件上传的两张图片仅剩一张了。
云空间文件上传
关于笑小枫💕
本章到这里结束了,喜欢的朋友关注一下我呦,大伙的支持,就是我坚持写下去的动力。
笑小枫个人博客:https://www.xiaoxiaofeng.com