「本文已参与好文召集令活动,点击查看:后端、大前端双赛道投稿,2万元奖池等你挑战!」
需求简介
最近在工作中有遇到这样一个需求:把某项业务的图片数据保存到本地三个月,在我们的工作中,这样类似的需求比较普遍,所以我这次分享一下我的一个做法,给大家作为这样一个参考。
具体需求
xxx业务里面有一张表,里面有一个这样的图片字段,需要把图片下载下来保存到本地,这个保存要求是在xxx根目录下建立对应日期文件夹,在对应日期文件夹里面再创建24个文件夹依次存储当天每半小时产生的图片数据。
我的思路
首先类似这种需求,白天的时候考虑到有用户在使用,为了不影响到性能等各方面的因素。我的一个想法是做一个定时任务,在每天晚上的0:05把三个月前保存的图片删除,循环查询并保存每半小时的图片数据。 ············································································································································· 为什么是0:05而不是00:00呢,因为我这边是直接用的日期API查询当天的日期来命名文件夹的。 ············································································································································· 定时任务这块我是直接使用SpringBoot的Scheduled,这类定时任务有一个弊端,就是如果你的服务在定时任务执行期间宕机了的话,它是没法重新再执行的,就这个需求而言,当天的图片就不会保存。所以需要怎么解决呢,其实的话可以使用Quartz。 ············································································································································· Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目,完全由Java开发,可以用来执行定时任务。这个框架有一个优点就是所有的定是任务都是存进数据库了的,假如你的服务宕机了,定时任务没有执行,当你的服务重启好了以后,是可以重试执行的。
实现功能
写定时任务之前我们先写两个方法,一个用来下载图片,一个用来删除指定文件夹。然后开启定时任务,编写定时任务方法。
开启定时任务
1.启动类添加注解开启定时任务@EnableScheduling
2.定时任务类添加@Compoment注解将类纳入spring bean管理
3.创建方法添加@Scheduled注解配置cron定时属性
cron表达式从左到右(用空格隔开):秒 分 小时 日期 月份 星期 年份
0 0 10,14,16 * * ? 每天上午10点,下午2点,4点每天上午10点,下午2点,4点 0 0/30 9-17 * * ? 朝九晚五工作时间内每半小时 0 0 12 ? * WED 表示每个星期三中午12点 0 0 12 * * ? 每天中午12点触发 0 15 10 ? * * 每天上午10:15触发 0 15 10 * * ? 每天上午10:15触发 0 15 10 * * ? * 每天上午10:15触发 0 15 10 * * ? 2019 2019年的每天上午10:15触发 0 * 14 * * ? 在每天下午2点到下午2:59期间的每1分钟触发 0 0/5 14 * * ? 在每天下午2点到下午2:55期间的每5分钟触发 0 0/5 14,18 * * ? 在每天下午2点到2:55期间和下午6点到6:55期间的每5分钟触发 0 0-5 14 * * ? 在每天下午2点到下午2:05期间的每1分钟触发 0 10,44 14 ? 3 WED 每年三月的星期三的下午2:10和2:44触发 0 15 10 ? * MON-FRI 周一至周五的上午10:15触发 0 15 10 15 * ? 每月15日上午10:15触发 0 15 10 L * ? 每月最后一日的上午10:15触发 0 15 10 ? * 6L 每月的最后一个星期五上午10:15触发 0 15 10 ? * 6L 2018-2019 2018年至2019年的每月的最后一个星期五上午10:15触发 0 15 10 ? * 6#3 每月的第三个星期五上午10:15触发
下载图片,并按照指定的路径存储
遇到的小坑:超时时间一定要在打开连接后,拿到输入流之前进行设置,否则是不生效的。
/**
* 下载图片,并按照指定的路径存储
*
* @param url 图片路径
* @param folderName 文件夹名
* @param fileName 文件名
*/
public static void downloadImages(String url, String folderName, String fileName) throws IOException {
try {
URL url1 = new URL(url);
URLConnection uc = url1.openConnection();
//连接超时时间
uc.setConnectTimeout(3000);
//读取超时时间
uc.setReadTimeout(3000);
InputStream inputStream = uc.getInputStream();
FileOutputStream out = new FileOutputStream(folderName + "\\" + fileName + ".png");
int j;
while ((j = inputStream.read()) != -1) {
out.write(j);
}
log.info("文件夹:{} —— 图片:{} —— 下载完成...:", folderName, fileName);
inputStream.close();
} catch (SocketTimeoutException e) {
log.info("文件夹:{} —— 图片:{} —— 下载失败(访问超时)...:", folderName, fileName);
} catch (ConnectException e) {
log.info("文件夹:{} —— 图片:{} —— 下载失败(连接异常)...:", folderName, fileName);
}
}
删除指定目录文件夹
/**
* 删除指定目录文件夹
*
* @param file
*/
public static void delTempChild(File file) {
if (file.isDirectory()) {
//获取文件夹下所有子文件夹
String[] children = file.list();
//递归删除目录中的子目录下
for (int i = 0; i < children.length; i++) {
delTempChild(new File(file, children[i]));
}
}
// 目录空了,进行删除
file.delete();
}
定时任务
/**
* 定时任务:卡口图片保存本地三个月
* 每天0点05分保存前天图片
* 删除三个月前图片
*
* @throws ParseException
*/
@Scheduled(cron = "0 05 0 * * ?")
public void scheduleTask() throws ParseException {
//删除三个月前图片
String threeMonthsAgo = LocalDate.now().minusMonths(3).format(DateTimeFormatter.ofPattern("yyyyMMdd"));
File deleteFilePath = new File("D:\\xxx\\imgs\\" + threeMonthsAgo);
delTempChild(deleteFilePath);
log.info("删除文件夹路径:{}", deleteFilePath);
//根据日期创建当天的文件夹
String data = LocalDate.now().minusDays(1).format(DateTimeFormatter.ofPattern("yyyyMMdd"));
//图片存放根目录
File file = new File("D:\\xxx\\imgs\\" + data);
//如果没有则创建
if (!file.exists()) {
file.mkdir();
//查询所需时间参数
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
long current = System.currentTimeMillis();
long zero = current / (1000 * 3600 * 24) * (1000 * 3600 * 24) - TimeZone.getDefault().getRawOffset();
Date date = new Date(zero - (24 * 60 * 60 * 1000));
Calendar calendar1 = Calendar.getInstance();
calendar1.setTime(date);
Calendar calendar2 = Calendar.getInstance();
calendar2.setTime(date);
calendar2.add(Calendar.MINUTE, 30);
//查询每半小时数据依次放入文件夹
int len = 24;
for (int i = 0; i < len; i++) {
//在当天文件夹下创建文件夹
File file1 = new File(file + "\\" + i);
file1.mkdir();
log.info("新生成文件夹路径:{}", file1);
List<JSONObject> xxxService;
if (i != 1) {
calendar1.add(Calendar.MINUTE, 30);
calendar2.add(Calendar.MINUTE, 30);
}
//查询每半小时数据
lists = xxxService.selectxxx(sdf.format(calendar1.getTimeInMillis()), sdf.format(calendar2.getTimeInMillis()));
if (!lists.isEmpty()) {
lists.forEach(t -> {
try {
downloadImages(t.getString("imagepath"), file1.toString(), t.getString("id"));
} catch (IOException e) {
e.printStackTrace();
}
});
}
}
}
}
总结
以上就是我分享的全部内容 ,以及我遇到的小坑,文章中有错误的地方或者大家有更好的做法还请评论区多多指教。