前言
当我们开发一个项目的时候,总会有需要文件操作的模块,本篇就进行Spring boot的文件上传下载的编写
操作流程
1、引入依赖
<!--文件上传下载依赖-->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
2、新建FileUtil类
@Slf4j
public class FileUtil {
/**
* 文件上传路径前缀
*/
public static String filePrefix;
/**
* 本地磁盘目录
*/
public static String uploadFile;
/**
* @Title: uploadFile
* @Description: 单文件上传到本地磁盘
* @param: multipartFile
* @return: java.lang.String
* @throws:
*/
public static String uploadFile(MultipartFile multipartFile) {
if (multipartFile == null) {
return null;
}
//生成文件名称,以免上传相同文件异常
String fileName = getUploadFileName(multipartFile.getOriginalFilename());
log.info(multipartFile.getOriginalFilename());
/**
// 获取当前日期
String dateDir = DateUtil.format(null, DateUtil.PATTERN_yyyyMMdd);
// 如果是今天第一次上传,则生成日期文件夹
File destFileDir = new File(uploadLocalPath + File.separator + dateDir);
// 文件夹不存在时,创建文件夹
if (!destFileDir.exists()) {
destFileDir.mkdirs();
}
**/
try {
// 获取上传后文件对象
File destFile = new File(FileUtil.uploadFile + File.separator + fileName);
/**
* destFileDir.getAbsoluteFile():当前项目与src文件夹的绝对地址一致
**/
// 上传文件到磁盘指定位置
multipartFile.transferTo(destFile);
log.info("文件【" + multipartFile.getOriginalFilename() + "】上传成功");
return fileName;
} catch (IOException e) {
log.error("文件上传异常:" + multipartFile.getOriginalFilename(), e);
return null;
}
}
/**
* @Title: getUploadFilePath
* @Description: 获取上传后的文件相对路径 --数据库存储该路径
* @param: fileName
* @return: java.lang.String
* @throws:
*/
public static String getUploadFileName(String fileName) {
//20210626093729817 + _ + HU5WMH + .jpg
return new StringBuilder()
.append(DateUtil.format(null, DateUtil.PATTERN_yyyyMMddHHmmssSSS))
.append("_").append(getRandomStrByNum(6))
.append(getExtension(fileName))
.toString();
}
/**
* 获取文件扩展名
*
* @param fileName
* @return
*/
public static String getExtension(String fileName) {
if (StringUtils.isEmpty(fileName)) {
return null;
}
return fileName.substring(fileName.lastIndexOf("."));
}
public static String CHAR_STR = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
/**
* @Title: getRandomStrByNum
* @Description: 获取不同位数的随机字符串
* @Author: lxt
* @param: factor
* @return: java.lang.String
* @throws:
*/
public static String getRandomStrByNum(int factor) {
// 拼接字符串
StringBuilder sb = new StringBuilder();
// java随机数对象
Random random = new Random();
for (int i = 0; i < factor; i++) {
int index = random.nextInt(36);
System.out.println("========>:" + index);
char c = CHAR_STR.charAt(index);
System.out.println("========>:" + c);
sb.append(c);
}
return sb.toString();
}
3、新建DateUtil类(当然,你可以选择不建这个类)
public class DateUtil {
/**
* 日志
*/
private static final Logger logger = LoggerFactory.getLogger(DateUtil.class);
/**
* date format yyyy-MM-dd HH:mm:ss
*/
public static final String PATTERN_24_h = "yyyy-MM-dd HH:mm:ss";
/**
* date format PATTERN_yyyyMMddHHmmss
*/
public static final String PATTERN_yyyyMMddHHmmss = "yyyyMMddHHmmss";
/**
* date format yyyyMMddHHmmssSSS
*/
public static final String PATTERN_yyyyMMddHHmmssSSS = "yyyyMMddHHmmssSSS";
/**
* date format yyyyMMdd
*/
public static final String PATTERN_yyyyMMdd = "yyyyMMdd";
public static final String PATTERN_yyyy_MM_dd = "yyyy-MM-dd";
public static final String PATTERN_hhmmss = "hh:mm:ss";
/**
* date format yyyyMMdd
*/
public static final String PATTERN_MMDD = "MMDD";
/**
* 月索引获取对应季度(月份减1),第一个0,用于占位
*/
public static final int [] QUARTERS = {0,1,1,1,2,2,2,3,3,3,4,4,4};
/**
* 枚举时间单位
*/
enum TimeUnits{
YEAR,MONTH,DAY,HOUR,MINUTES,SECOND
}
/**
* LocalDateTime->Date
* @param localDateTime
* @return
*/
public static Date local2Date(LocalDateTime localDateTime){
return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
}
/**
*LocalDate->Date
* @param localDate
* @return
*/
public static Date local2Date(LocalDate localDate){
return Date.from(localDate.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant());
}
/**
*LocalDate->Date
* @param localTime
* @return
*/
public static Date local2Date(LocalTime localTime){
return Date.from(LocalDateTime.of(LocalDate.now(), localTime).atZone(ZoneId.systemDefault()).toInstant());
}
/**
*Date->LocalDate
* @param date
* @return
*/
public static LocalDate date2LocalDate(Date date){
return LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault()).toLocalDate();
}
/**
*Date->LocalDate
* @param date
* @return
*/
public static LocalDateTime date2LocalDateTime(Date date){
return LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
}
/**
*Date->LocalTime
* @param date
* @return
*/
public static LocalTime date2LocalTime(Date date){
return LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault()).toLocalTime();
}
/**
* @Title: plusDate
* @Description: 根据偏移量(大于0后移,小于0前移)获取时间
* @param: date 基准时间,null为当前时间
* @param: chronoUnit 单位
* @param: count 数值
* @return: java.time.LocalDateTime
* @throws:
*/
public static LocalDateTime plusDate(LocalDateTime localDateTime, ChronoUnit chronoUnit, int count){
//默认取当前时间
localDateTime = localDateTime == null ? LocalDateTime.now() : localDateTime;
switch (chronoUnit){
case YEARS:
return localDateTime.plusYears(count);
case MONTHS:
return localDateTime.plusMonths(count);
case DAYS:
return localDateTime.plusDays(count);
case HOURS:
return localDateTime.plusHours(count);
case MINUTES:
return localDateTime.plusMinutes(count);
case SECONDS:
return localDateTime.plusSeconds(count);
case NANOS:
return localDateTime.plusNanos(count);
default:
return localDateTime.plusYears(count);
}
}
/**
* 获取某季度第一天
* @param quarter eg:1、2、3、4季度
* @return
* 成功:返回正确的日期
* 失败:返回null
*/
public static LocalDate getQuarterOfFirstDay(int quarter){
switch (quarter){
case 1:
return LocalDate.of(LocalDate.now().getYear(),1,1);
case 2:
return LocalDate.of(LocalDate.now().getYear(),4,1);
case 3:
return LocalDate.of(LocalDate.now().getYear(),7,1);
case 4:
return LocalDate.of(LocalDate.now().getYear(),10,1);
default:
return null;
}
}
/**
*获取某季度最后一天
* @param quarter:1、2、3、4季度
* @return
* 成功:返回正确的日期
* 失败:返回null
*/
public static LocalDate getQuarterOfLastDay(int quarter){
switch (quarter){
case 1:
return LocalDate.of(LocalDate.now().getYear(),3,1).with(TemporalAdjusters.lastDayOfMonth());
case 2:
return LocalDate.of(LocalDate.now().getYear(),6,1).with(TemporalAdjusters.lastDayOfMonth());
case 3:
return LocalDate.of(LocalDate.now().getYear(),9,1).with(TemporalAdjusters.lastDayOfMonth());
case 4:
return LocalDate.of(LocalDate.now().getYear(),12,1).with(TemporalAdjusters.lastDayOfMonth());
default:
return null;
}
}
/**
* 格式化时间
* @param localDateTime
* @param pattern 格式
* @return
*/
public static String format(LocalDateTime localDateTime, String pattern){
//默认取当前时间
localDateTime = localDateTime == null ? LocalDateTime.now() : localDateTime;
return localDateTime.format(DateTimeFormatter.ofPattern(pattern));
}
/**
* @Title: getCurrentTime
* @Description: 获取当前时间
* @param: pattern
* @return: java.lang.String
* @throws:
*/
public static String getCurrentTime(String pattern){
return format(null,pattern);
}
/**
* 获取日期时间差endDate-startDate
* @param startDate
* @param endDate
* @return
* startDate before 0f endDate =>正数
* startDate after 0f endDate =>负数
*/
public static long bewteenTwoDays(LocalDate startDate, LocalDate endDate){
return ChronoUnit.DAYS.between(startDate, endDate);
}
/**
* 获取时间差endDateTime-startDateTime
* @param startDateTime
* @param endDateTime
* @param chronoUnit ChronoUnit枚举类型
* @return
* startDateTime before 0f endDateTime =>正数
* startDateTime after 0f endDateTime =>负数
*/
public static long bewteenTwoTimes(LocalDateTime startDateTime, LocalDateTime endDateTime, ChronoUnit chronoUnit){
switch (chronoUnit){
case YEARS:
return ChronoUnit.YEARS.between(startDateTime, endDateTime);
case MONTHS:
return ChronoUnit.MONTHS.between(startDateTime, endDateTime);
case DAYS:
return ChronoUnit.DAYS.between(startDateTime, endDateTime);
case HOURS:
return ChronoUnit.HOURS.between(startDateTime, endDateTime);
case MINUTES:
return ChronoUnit.MINUTES.between(startDateTime, endDateTime);
case SECONDS:
return ChronoUnit.SECONDS.between(startDateTime, endDateTime);
case NANOS:
return ChronoUnit.NANOS.between(startDateTime, endDateTime);
default:
return 0;
}
}
/**
*
* @Title: differentDays
* @Description: TODO date2比date1多的天数
* @param @param date1
* @param @param date2
* @param @return
* @return int
* @throws
*/
public static int differentDays(Date date1, Date date2)
{
Calendar cal1 = Calendar.getInstance();
cal1.setTime(date1);
Calendar cal2 = Calendar.getInstance();
cal2.setTime(date2);
int day1= cal1.get(Calendar.DAY_OF_YEAR);
int day2 = cal2.get(Calendar.DAY_OF_YEAR);
int year1 = cal1.get(Calendar.YEAR);
int year2 = cal2.get(Calendar.YEAR);
if(year1 != year2) //同一年
{
int timeDistance = 0 ;
for(int i = year1 ; i < year2 ; i ++)
{
if(i%4==0 && i%100!=0 || i%400==0) //闰年
{
timeDistance += 366;
}
else //不是闰年
{
timeDistance += 365;
}
}
return timeDistance + (day2-day1) ;
}
else //不同年
{
// System.out.println("判断day2 - day1 : " + (day2-day1));
return day2-day1;
}
}
}
4、在WebMvcConfig类中添加如下代码
/**
* 文件上传路径前缀
*/
@Value("${application.filePrefix}")
public String filePrefix;
/**
* 本地磁盘目录
*/
@Value("${application.uploadFile}")
public String uploadFile;
/**
* @Title: addResourceHandlers
* @Description: 映射本地磁盘为静态目录
* @param: registry
* @throws:
*/
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry){
FileUtil.filePrefix = filePrefix;
FileUtil.uploadFile = uploadFile;
registry.addResourceHandler(filePrefix +"/**")
.addResourceLocations("file:"+uploadFile);
}
5、配置yml文件
spring:
# 文件上传下载相关配置
servlet:
multipart:
enabled: true
max-file-size: 20MB
max-request-size: 20MB
application:
#文件上传前缀
filePrefix: /
#文件上传路径
uploadFile: C:/Users/14193/Desktop/待办事项/Spring boot文件操作/
6、编码controller文件
@Slf4j
@RestController
@RequestMapping("file")
public class FileController {
@Resource
private FileService fileService;
@Resource
private HttpServletRequest request;
@Resource
private SysUserService sysUserService;
/**
* 上传文件
*
* @param file
* @return
*/
@ResponseBody
@RequestMapping("upload")
public ResultJson upload(@RequestParam("file") MultipartFile file) throws Exception {
if (file == null) {
return ResultJson.error("文件上传失败");
}
Map<String, String> r = new HashMap<>();
// 调用上传方法返回可访问的路径
String filePath = FileUtil.uploadFile(file);
// 上传后的路径
r.put("filePath", filePath);
// 文件原名
r.put("fileName", file.getOriginalFilename());
// 文件大小
r.put("fileSize", file.getSize() + "");
/**
* 相应的数据入数据库
**/
FileOperation uploadFile = new FileOperation();
uploadFile.setFileName(file.getOriginalFilename());
uploadFile.setUploadTime(LocalDateTime.now());
String token =request.getHeader("token");
Long uid = Long.valueOf(JwtUtil.getAudience(token));
uploadFile.setUserId(uid);
uploadFile.setFilePath(filePath);
fileService.save(uploadFile);
SysUser user=new SysUser();
user.setId(uid);
user.setPhoto(filePath);
sysUserService.saveOrUpdate(user);
return ResultJson.ok("文件上传成功", r);
}
@NoNeedToken
@ResponseBody
@RequestMapping("downloadByFilePath")
public ResponseEntity<byte[]> download(String filePath) throws IOException {
if (StringUtils.isEmpty(filePath)) {
throw new RuntimeException("路径不可为空!");
}
/**
* 获取磁盘路径
*/
// 磁盘根路径 + 相对路径 获取绝对路径
String localPath = FileUtil.uploadFile + filePath;
log.info("**********----------"+localPath);
File file = new File(localPath);
HttpHeaders headers = new HttpHeaders();
headers.setContentDispositionFormData("attachment",
new String(file.getName().getBytes(StandardCharsets.UTF_8), "iso-8859-1"));
headers.add("Access-Control-Expose-Headers", "Content-Disposition");
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
// 获取文件的字节数组 - 需要使用commons-io依赖包
byte[] content = FileUtils.readFileToByteArray(file);
// 返回下载的二进制内容
return new ResponseEntity<>(content, headers, HttpStatus.OK);
}
}
测试
上传:
下载: