工作分享 ——(需求:图片保存到本地三个月)

268 阅读5分钟

「本文已参与好文召集令活动,点击查看:后端、大前端双赛道投稿,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();
                        }
                    });
                }
            }
        }
    }

总结

以上就是我分享的全部内容 ,以及我遇到的小坑,文章中有错误的地方或者大家有更好的做法还请评论区多多指教。