SpringBoot单文件与多文件上传
本次例子不基于第三方存储(如七牛云对象存储、阿里云对象存储、腾讯云对象存储等),仅基于本地存储。
单文件上传主要应用场景如上传头像、特定文件转换、授权文件验证等。
多文件上传主要应用场景如批量Excel文件数据录入等。
本文主要内容如下:
- 公共文件存储代码;
- 单文件上传代码;
- 多文件上传代码。
一、公共文件存储代码
1.FileUploadUtils.java
package com.springcloud.blog.admin.util.file; /** * @description: 文件上传工具类 * @author: youcong * @time: 2020/9/28 11:34 */ import com.springcloud.blog.admin.exception.file.FileNameLengthLimitExceededException; import com.springcloud.blog.admin.exception.file.FileSizeLimitExceededException; import com.springcloud.blog.admin.exception.file.InvalidExtensionException; import com.springcloud.blog.admin.util.DateUtils; import com.springcloud.blog.admin.util.IdUtils; import com.springcloud.blog.admin.util.StringUtils; import org.apache.commons.fileupload.FileUploadBase; import org.apache.commons.io.FilenameUtils; import org.springframework.web.multipart.MultipartFile; import java.io.File; import java.io.IOException; public class FileUploadUtils { /** * 默认大小 50M */ public static final long DEFAULT_MAX_SIZE = 50 * 1024 * 1024; /** * 默认的文件名最大长度 100 */ public static final int DEFAULT_FILE_NAME_LENGTH = 100; /** * 默认上传的地址 */ private static String defaultBaseDir = "D://test//"; /** * 资源映射路径 前缀 */ public static String resource_prefix = "D://test//"; public static void setDefaultBaseDir(String defaultBaseDir) { FileUploadUtils.defaultBaseDir = defaultBaseDir; } public static String getDefaultBaseDir() { return defaultBaseDir; } /** * 以默认配置进行文件上传 * * @param file 上传的文件 * @return 文件名称 * @throws Exception */ public static final String upload(MultipartFile file) throws IOException { try { return upload(getDefaultBaseDir(), file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION); } catch (Exception e) { throw new IOException(e.getMessage(), e); } } /** * 文件上传 * * @param baseDir 相对应用的基目录 * @param file 上传的文件 * @param allowedExtension 上传文件类型 * @return 返回上传成功的文件名 * @throws FileSizeLimitExceededException 如果超出最大大小 * @throws FileNameLengthLimitExceededException 文件名太长 * @throws IOException 比如读写文件出错时 * @throws InvalidExtensionException 文件校验异常 */ public static final String upload(String baseDir, MultipartFile file, String[] allowedExtension) throws FileUploadBase.FileSizeLimitExceededException, IOException, FileNameLengthLimitExceededException, InvalidExtensionException { int fileNamelength = file.getOriginalFilename().length(); if (fileNamelength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH) { throw new FileNameLengthLimitExceededException(FileUploadUtils.DEFAULT_FILE_NAME_LENGTH); } assertAllowed(file, allowedExtension); String fileName = extractFilename(file); File desc = getAbsoluteFile(baseDir, fileName); file.transferTo(desc); String pathFileName = getPathFileName(baseDir, fileName); return pathFileName; } /** * 编码文件名 */ public static final String extractFilename(MultipartFile file) { String fileName = file.getOriginalFilename(); String extension = getExtension(file); fileName = DateUtils.datePath() + "/" + IdUtils.fastUUID() + "." + extension; return fileName; } private static final File getAbsoluteFile(String uploadDir, String fileName) throws IOException { File desc = new File(uploadDir + File.separator + fileName); if (!desc.getParentFile().exists()) { desc.getParentFile().mkdirs(); } if (!desc.exists()) { desc.createNewFile(); } return desc; } private static final String getPathFileName(String uploadDir, String fileName) throws IOException { int dirLastIndex = defaultBaseDir.length() + 1; String currentDir = StringUtils.substring(uploadDir, dirLastIndex); String pathFileName = resource_prefix + "/" + currentDir + "/" + fileName; return pathFileName; } /** * 文件大小校验 * * @param file 上传的文件 * @return * @throws FileSizeLimitExceededException 如果超出最大大小 * @throws InvalidExtensionException */ public static final void assertAllowed(MultipartFile file, String[] allowedExtension) throws FileSizeLimitExceededException, InvalidExtensionException { long size = file.getSize(); if (DEFAULT_MAX_SIZE != -1 && size > DEFAULT_MAX_SIZE) { throw new FileSizeLimitExceededException(DEFAULT_MAX_SIZE / 1024 / 1024); } String fileName = file.getOriginalFilename(); String extension = getExtension(file); if (allowedExtension != null && !isAllowedExtension(extension, allowedExtension)) { if (allowedExtension == MimeTypeUtils.IMAGE_EXTENSION) { throw new InvalidExtensionException.InvalidImageExtensionException(allowedExtension, extension, fileName); } else if (allowedExtension == MimeTypeUtils.FLASH_EXTENSION) { throw new InvalidExtensionException.InvalidFlashExtensionException(allowedExtension, extension, fileName); } else if (allowedExtension == MimeTypeUtils.MEDIA_EXTENSION) { throw new InvalidExtensionException.InvalidMediaExtensionException(allowedExtension, extension, fileName); } else if (allowedExtension == MimeTypeUtils.VIDEO_EXTENSION) { throw new InvalidExtensionException.InvalidVideoExtensionException(allowedExtension, extension, fileName); } else { throw new InvalidExtensionException(allowedExtension, extension, fileName); } } } /** * 判断MIME类型是否是允许的MIME类型 * * @param extension * @param allowedExtension * @return */ public static final boolean isAllowedExtension(String extension, String[] allowedExtension) { for (String str : allowedExtension) { if (str.equalsIgnoreCase(extension)) { return true; } } return false; } /** * 获取文件名的后缀 * * @param file 表单文件 * @return 后缀名 */ public static final String getExtension(MultipartFile file) { String extension = FilenameUtils.getExtension(file.getOriginalFilename()); if (StringUtils.isEmpty(extension)) { extension = MimeTypeUtils.getExtension(file.getContentType()); } return extension; } }
2.FileUtils.java
package com.springcloud.blog.admin.util.file; import javax.servlet.http.HttpServletRequest; import java.io.*; import java.net.URLEncoder; /** * 文件处理工具类 * * @author youcong */ public class FileUtils extends org.apache.commons.io.FileUtils { public static String FILENAME_PATTERN = "[a-zA-Z0-9_\\-\\|\\.\\u4e00-\\u9fa5]+"; /** * 输出指定文件的byte数组 * * @param filePath 文件路径 * @param os 输出流 * @return */ public static void writeBytes(String filePath, OutputStream os) throws IOException { FileInputStream fis = null; try { File file = new File(filePath); if (!file.exists()) { throw new FileNotFoundException(filePath); } fis = new FileInputStream(file); byte[] b = new byte[1024]; int length; while ((length = fis.read(b)) > 0) { os.write(b, 0, length); } } catch (IOException e) { throw e; } finally { if (os != null) { try { os.close(); } catch (IOException e1) { e1.printStackTrace(); } } if (fis != null) { try { fis.close(); } catch (IOException e1) { e1.printStackTrace(); } } } } /** * 删除文件 * * @param filePath 文件 * @return */ public static boolean deleteFile(String filePath) { boolean flag = false; File file = new File(filePath); // 路径为文件且不为空则进行删除 if (file.isFile() && file.exists()) { file.delete(); flag = true; } return flag; } /** * 文件名称验证 * * @param filename 文件名称 * @return true 正常 false 非法 */ public static boolean isValidFilename(String filename) { return filename.matches(FILENAME_PATTERN); } /** * 下载文件名重新编码 * * @param request 请求对象 * @param fileName 文件名 * @return 编码后的文件名 */ public static String setFileDownloadHeader(HttpServletRequest request, String fileName) throws UnsupportedEncodingException { final String agent = request.getHeader("USER-AGENT"); String filename = fileName; if (agent.contains("MSIE")) { // IE浏览器 filename = URLEncoder.encode(filename, "utf-8"); filename = filename.replace("+", " "); } else if (agent.contains("Firefox")) { // 火狐浏览器 filename = new String(fileName.getBytes(), "ISO8859-1"); } else if (agent.contains("Chrome")) { // google浏览器 filename = URLEncoder.encode(filename, "utf-8"); } else { // 其它浏览器 filename = URLEncoder.encode(filename, "utf-8"); } return filename; } }
3.MimeTypeUtils.java
package com.springcloud.blog.admin.util.file; /** * @description: * @author: youcong * @time: 2020/9/28 11:37 */ public class MimeTypeUtils { public static final String IMAGE_PNG = "image/png"; public static final String IMAGE_JPG = "image/jpg"; public static final String IMAGE_JPEG = "image/jpeg"; public static final String IMAGE_BMP = "image/bmp"; public static final String IMAGE_GIF = "image/gif"; public static final String[] IMAGE_EXTENSION = {"bmp", "gif", "jpg", "jpeg", "png"}; public static final String[] FLASH_EXTENSION = {"swf", "flv"}; public static final String[] MEDIA_EXTENSION = {"swf", "flv", "mp3", "wav", "wma", "wmv", "mid", "avi", "mpg", "asf", "rm", "rmvb"}; public static final String[] VIDEO_EXTENSION = {"mp4", "avi", "rmvb"}; public static final String[] DEFAULT_ALLOWED_EXTENSION = { // 图片 "bmp", "gif", "jpg", "jpeg", "png", // word excel powerpoint "doc", "docx", "xls", "xlsx", "ppt", "pptx", "html", "htm", "txt", // 压缩文件 "rar", "zip", "gz", "bz2", // 视频格式 "mp4", "avi", "rmvb", // pdf "pdf"}; public static String getExtension(String prefix) { switch (prefix) { case IMAGE_PNG: return "png"; case IMAGE_JPG: return "jpg"; case IMAGE_JPEG: return "jpeg"; case IMAGE_BMP: return "bmp"; case IMAGE_GIF: return "gif"; default: return ""; } } }
4.FileException.java
package com.springcloud.blog.admin.exception.file; import com.springcloud.blog.admin.exception.BaseException; /** * @description: 文件信息异常类 * @author: youcong * @time: 2020/9/28 11:41 */ public class FileException extends BaseException { private static final long serialVersionUID = 1L; public FileException(String code, Object[] args) { super("file", code, args, null); } }
5.FileNameLengthLimitExceededException.java
package com.springcloud.blog.admin.exception.file; /** * @description:文件名称超长限制异常类 * @author: youcong * @time: 2020/9/28 11:41 */ public class FileNameLengthLimitExceededException extends FileException { private static final long serialVersionUID = 1L; public FileNameLengthLimitExceededException(int defaultFileNameLength) { super("upload.filename.exceed.length", new Object[]{defaultFileNameLength}); } }
6.FileSizeLimitExceededException.java
package com.springcloud.blog.admin.exception.file; /** * @description: 文件名大小限制异常类 * @author: youcong * @time: 2020/9/28 11:42 */ public class FileSizeLimitExceededException extends FileException { private static final long serialVersionUID = 1L; public FileSizeLimitExceededException(long defaultMaxSize) { super("upload.exceed.maxSize", new Object[]{defaultMaxSize}); } }
7.InvalidExtensionException.java
package com.springcloud.blog.admin.exception.file; /** * @description: 文件上传错误异常类 * @author: youcong * @time: 2020/9/28 11:42 */ import org.apache.commons.fileupload.FileUploadException; import java.util.Arrays; public class InvalidExtensionException extends FileUploadException { private static final long serialVersionUID = 1L; private String[] allowedExtension; private String extension; private String filename; public InvalidExtensionException(String[] allowedExtension, String extension, String filename) { super("filename : [" + filename + "], extension : [" + extension + "], allowed extension : [" + Arrays.toString(allowedExtension) + "]"); this.allowedExtension = allowedExtension; this.extension = extension; this.filename = filename; } public String[] getAllowedExtension() { return allowedExtension; } public String getExtension() { return extension; } public String getFilename() { return filename; } public static class InvalidImageExtensionException extends InvalidExtensionException { private static final long serialVersionUID = 1L; public InvalidImageExtensionException(String[] allowedExtension, String extension, String filename) { super(allowedExtension, extension, filename); } } public static class InvalidFlashExtensionException extends InvalidExtensionException { private static final long serialVersionUID = 1L; public InvalidFlashExtensionException(String[] allowedExtension, String extension, String filename) { super(allowedExtension, extension, filename); } } public static class InvalidMediaExtensionException extends InvalidExtensionException { private static final long serialVersionUID = 1L; public InvalidMediaExtensionException(String[] allowedExtension, String extension, String filename) { super(allowedExtension, extension, filename); } } public static class InvalidVideoExtensionException extends InvalidExtensionException { private static final long serialVersionUID = 1L; public InvalidVideoExtensionException(String[] allowedExtension, String extension, String filename) { super(allowedExtension, extension, filename); } } }
8.BaseException.java
package com.springcloud.blog.admin.exception; /** * 基础异常 * * @author youcong */ public class BaseException extends RuntimeException { private static final long serialVersionUID = 1L; /** * 所属模块 */ private String module; /** * 错误码 */ private String code; /** * 错误码对应的参数 */ private Object[] args; /** * 错误消息 */ private String defaultMessage; public BaseException(String module, String code, Object[] args, String defaultMessage) { this.module = module; this.code = code; this.args = args; this.defaultMessage = defaultMessage; } public BaseException(String module, String code, Object[] args) { this(module, code, args, null); } public BaseException(String module, String defaultMessage) { this(module, null, null, defaultMessage); } public BaseException(String code, Object[] args) { this(null, code, args, null); } public BaseException(String defaultMessage) { this(null, null, null, defaultMessage); } public String getModule() { return module; } public String getCode() { return code; } public Object[] getArgs() { return args; } public String getDefaultMessage() { return defaultMessage; } }
二、单文件上传代码
@PostMapping("/post/uploadFile") @ApiOperation("文章上传特色图片") public ResponseBaseDTO<String> uploadFile(@RequestParam("file") MultipartFile file) { logger.info("/post/uploadFile"); try { String fileUrl = FileUploadUtils.upload(file); if (fileUrl != null) { return ResponseBaseDTO.createSuccResp(fileUrl); } return ResponseBaseDTO.createFailResp(fileUrl); } catch (Exception e) { logger.error("/post/uploadFile", e); return ResponseBaseDTO.createFailResp(e.getMessage()); } }
三、多文件上传代码
@PostMapping("/batchImportsUsers") @ApiOperation("批量导入用户数据小时") public ResponseBaseDTO<String> batchImportsUsers(MultipartFile[] uploadFiles) { if (uploadFiles.length > 0) { for (int i = 0; i < uploadFiles.length; i++) { try { importUserExcelData(uploadFiles[i]); } catch (Exception e) { e.printStackTrace(); } } return ResponseBaseDTO.createSuccResp(); } return ResponseBaseDTO.createFailResp(e.getMessage()); }