本博客可实现文件,图片等上传,代码可直接调用service层upload方法,可参考上一篇博客图片实现的简单上传
前言
- 文件上传下载应用场景很多。比如:我们在修改头像的时候,需要上传头像;我们在后台修改商品信息的时候,也需要上传商品图片。作为Java开发者,文件上传与文件下载功能已经是必会的技能。
- 这次使用SpringBoot实现单文件上传,多文件上传以及文件下载,重点介绍MultipartFile工具类。
文件上传到哪里合适?
-
将文件上传到工程目录下:在一些文件存储量很小的工程中,有一些上传文件放置在工程本身的目录下,但是随着文件上传的量越来越大,工程本身所在的文件夹容量会越来越大,不仅打包和部署的效率会降低,工程的启动和运行也会变慢,所以一般不会采用这做
-
将文件上传到工程所在服务器:将文件专门上传到Web应用工程所在容器(如Tomcat)位于的服务器中,单独开辟一个盘符或文件夹用于存储上传的图片,这种做法让上传 文件与工程本身分离,工程的打包和启动效率不受到任何影响。但是如果以后出现了海量图片,Web应用工程所在的服务器的效率会降低,这样也会间接地降低应用的执行效率,所以在上传图片量不大的情况下,可以采用该做法。
-
搭建文件服务器:一般大型的互联网项目,都会为自己的文件上传单独架设一个文件服务器(有集群的应用,可能会有多台文件服务器),也有独立处理文件上传、文件访问的服务器。这种方案就是太烧钱。
上面分析了三种方案的特点和优缺点。第一种一般不采取,第二种可能会采取,最常用的就是第三种方案。
MultipartFile工具类
MultipartFile是SpringMVC提供简化上传操作的工具类。
在不使用框架之前,都是使用原生的HttpServletRequest来接收上传的数据,文件是以二进制流传递到后端的,然后需要我们自己转换为File类,非常麻烦。使用了MultipartFile工具类之后,我们对文件上传的操作就简便许多了。
以下是MultipartFile的框架层面的封装
controller层
package *.controller;
import *.framework.web.message.MessageBody;
import *.framework.web.message.MessageCode;
import *.framework.web.upload.model.UploadRequest;
import *.framework.web.upload.properties.UploadProperties;
import *.framework.web.upload.service.IUploadService;
import *.framework.web.upload.utils.UploadUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.*;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.nio.charset.Charset;
/**
* 上传文件基础类
*/
@Slf4j
public abstract class AbstractUploadController {
@Autowired
protected IUploadService uploadService;
@Autowired
private UploadProperties uploadProperties;
/**
* 预上传,返回上传所需参数
*
* @param uploadRequest 上传请求
* @return httpResponse
*/
@GetMapping("/upload/preload")
public MessageBody<?> preload(UploadRequest uploadRequest) {
return MessageCode.SUCCESS.data(uploadService.preload(uploadRequest));
}
/**
* 上传文件
*
* @param uploadFile 文件
* @param uploadRequest 上传请求
* @param token 签名字符串
* @return httpResponse
*/
@PostMapping("/upload")
public MessageBody<?> upload(@RequestParam("file") MultipartFile uploadFile, UploadRequest uploadRequest, String token) {
try {
//判断是否需要限制
if (uploadProperties.getFileTypeLimit() != null && uploadProperties.getFileTypeLimit().size() > 0
) {
//检验上传文件类型是否支持
if (!UploadUtils.checkType(uploadProperties.getFileTypeLimit(), uploadFile)) {
return MessageCode.ERROR.message("上传文件类型不支持");
}
//检验上传文件大小是否支持
if (!UploadUtils.checkLimit(uploadProperties.getFileTypeLimit(), uploadFile)) {
return MessageCode.ERROR.message("上传文件大小超出限制");
}
}
return MessageCode.SUCCESS.data(uploadService.upload(uploadFile, uploadRequest, token));
} catch (IOException e) {
return MessageCode.ERROR.build();
}
}
/**
* 预览、下载上传的文件
*
* @param fileName 文件名称
* @return 文件流
*/
@GetMapping("/media/{fileName}/_download")
public ResponseEntity<?> download(@PathVariable String fileName) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
Charset charset = Charset.defaultCharset();
headers.setContentDisposition(ContentDisposition.builder("attachment").filename(fileName, charset).build());
return new ResponseEntity<>(uploadService.files(fileName), headers, HttpStatus.OK);
}
}
上传请求实体类
package *.web.upload.model;
import lombok.Data;
@Data
public class UploadRequest {
/**
* 是否覆盖 0.否 1.是
*/
private Integer isCover = 1;
/**
* 要存储的文件名称
*/
private String fileName;
/**
* 时间戳
*/
private long timestamp;
}
签名
@Data
@AllArgsConstructor
public class UploadToken {
private String sign;
private Long timestamp;
}
service层,抽象类
package*.web.upload.service;
import *
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.io.InputStream;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public abstract class IUploadService {
protected static final DateTimeFormatter FILE_NAME_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMddHHmmssSS");
protected IUploadService() {
}
/**
* 预上传,获取token
*
* @param uploadRequest 上传请求实体
* @return 签名
*/
public abstract UploadToken preload(UploadRequest uploadRequest);
/**
* 通过multipartFile上传文件,无token
*
* @param uploadFile 文件
* @param uploadRequest 请求实体
* @return 上传成功实体
* @throws IOException ioexception
*/
public UploadSuccess upload(MultipartFile uploadFile, UploadRequest uploadRequest) throws IOException {
return this.upload(uploadFile, uploadRequest, null);
}
/**
* 通过multipartFile上传文件,有token
*
* @param uploadFile 文件
* @param uploadRequest 请求实体
* @param token 签名字符串
* @return 上传成功实体
* @throws IOException ioexception
*/
public abstract UploadSuccess upload(MultipartFile uploadFile, UploadRequest uploadRequest, String token) throws IOException;
/**
* 通过stream上传文件,无token
*
* @param uploadStream 文件流
* @param uploadRequest 请求实体
* @return 上传成功实体
* @throws IOException ioexception
*/
public UploadSuccess upload(InputStream uploadStream, UploadRequest uploadRequest) throws IOException {
return this.upload(uploadStream, uploadRequest, null);
}
/**
* 通过stream上传文件,有token
*
* @param uploadStream 上传的stream流
* @param uploadRequest 请求实体
* @param token 签名字符串
* @return 上传成功实体
* @throws IOException ioexception
*/
public abstract UploadSuccess upload(InputStream uploadStream, UploadRequest uploadRequest, String token) throws IOException;
/**
* 通过字节数组上传文件,无token
*
* @param uploadByte 字节数组
* @param uploadRequest 请求实体
* @return 上传成功实体
* @throws IOException ioexception
*/
public UploadSuccess upload(byte[] uploadByte, UploadRequest uploadRequest) throws IOException {
return this.upload(uploadByte, uploadRequest, null);
}
/**
* 通过字节数组上传文件,有token
*
* @param uploadByte 上传的字节数组
* @param uploadRequest 请求实体
* @param token 签名字符串
* @return 上传成功实体
* @throws IOException ioexception
*/
public abstract UploadSuccess upload(byte[] uploadByte, UploadRequest uploadRequest, String token) throws IOException;
/**
* 根据上传参数获取文件名称
*
* @param uploadRequest 上传参数
* @return 文件名
*/
protected String getFileName(UploadRequest uploadRequest, MultipartFile multipartFile) {
String suffix = multipartFile != null ? Utils.fileSuffixWithPoint(multipartFile