Java后台服务版本升级之增量资源包打包功能开发(SpringBoot项目)

该代码示例展示了如何在SpringBoot项目中开发一个接口,用于打包和下载增量SQL以及对应的资源文件。服务首先遍历SQL文件,提取引用的资源路径,然后从FastDFS和Nginx服务器下载资源到本地,再进行压缩打包,供运维人员下载用于版本升级。
摘要由CSDN通过智能技术生成

项目场景:

        公司产品后台服务版本升级之增量资源包打包功能开发:每一次版本升级的增量sql(SpringBoot项目的resources下)增量sql相对应的资源(在资源云服务器上:Centos7系统)打包下载接口。

        涉及知识点:

  • 浏览器访问API,资源包直接下载本地磁盘
  • 访问resources下指定目录的文件
  • Java删除Linux文件
  • Zip文件打包(提供ZipUtil)

        业务:运维人员登录后台系统点击资源打包功能,下载压缩的资源zip包上传到另一个云服务器(专门用于版本升级的),用户的本地服务器可以定时检测同步是否有新版本,从而下载对应的资源包,话不多说,上代码:


Controller层:

@Api(tags = "版本与license Controller")
@RequestMapping("/version")
@RestController
public class VersionController extends BaseController {

    @Resource
    private VersionService versionService;   

    @ApiOperation(value = "生成资源压缩包")
    @GetMapping("/admin/generateResourceZip")
    @SysLog(value = "生成压缩资源包", isEdit = false)
    public void generateResourceZip(HttpServletResponse response) throws Exception {
        String resourcePath = versionService.generateResourceZip();
        String filename = URLEncoder.encode(resourcePath.substring(resourcePath.lastIndexOf("/")+1), "UTF-8");
        response.setContentType("application/x-download");
        response.setHeader("Content-Disposition", "attachment;filename=" + filename);//浏览器上提示下载时默认的文件名
        try (ServletOutputStream out = response.getOutputStream();
            InputStream stream = new FileInputStream(resourcePath)){//读取服务器上的文件
            byte buff[] = new byte[1024];
            int length = 0;
            while ((length = stream.read(buff)) > 0) {
                out.write(buff,0,length);
            }
            stream.close();
            out.close();
            out.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Service层:


    /**
     * @Author: yz
     * @Date 14:42 2023/1/7
     * @Param []
     * @return 版本资源打包功能
     */
    @Override
    public String generateResourceZip() throws Exception {
        //找到对应的增量sql文件
        //加载资源文件
        String path = Constants.RESOURCE_SQL_PATH;
        final Resource[] resources = new PathMatchingResourcePatternResolver().getResources(ResourceUtils.CLASSPATH_URL_PREFIX + path);
        log.info("开始扫描增量sql");
        //存储在fastdfs上的路径集合
        List<String> fastdfsList = new ArrayList<>();
        //存储在nginx下xxa目录下的路径集合
        List<String> nginxXxaList = new ArrayList<>();
        //存储在nginx下xxb目录下的路径集合
        List<String> nginxXxbList = new ArrayList<>();
        //存储在nginx下xxc目录下的路径集合
        List<String> nginxXxcList = new ArrayList<>();
        //存储在nginx下xxd目录下的路径集合
        List<String> nginxXxdList = new ArrayList<>();
        //读取增量sql文件的资源path内容
        for (int i = 0; i < resources.length; i++) {
            Resource resource = resources[i];
            // 遍历文件内容
            StringBuffer script = new StringBuffer();
            try(InputStreamReader isr = new InputStreamReader(resource.getInputStream(), StandardCharsets.UTF_8);
                BufferedReader bufferReader = new BufferedReader(isr)) {
                String tempString;
                while ((tempString = bufferReader.readLine()) != null) {
                    script.append(tempString).append("\n");
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            String content = script.toString();
            //判断资源存储于fastdfs、nginx下xxd、xxb、xxa、xxc下
            String[] fsatdfsSplit = content.split(Constants.FASTDFS_PATH);
            String[] xxaSplit = content.split(Constants.SPECIAL_INTERACTION_PATH);
            String[] xxbSplit = content.split(Constants.EXAM_INTERACTION_PATH);
            String[] xxcSplit = content.split(Constants.INTERACTION_UPLOAD_PATH);
            String[] xxdSplit = content.split(Constants.INTERACTION_ASSESS_PATH);

            if(fsatdfsSplit.length>1){
                fastdfsPackage(content,fastdfsList);
            }
            if(xxaSplit.length>1){
                for (int j = 0; j < xxaSplit.length; j++) {
                    if(j>0){
                        nginxXxaList.add(Constants.SPECIAL_INTERACTION_PATH+xxaSplit[j].substring(0,xxaSplit[j].indexOf("\'")));
                    }
                }
            }
            if(xxbSplit.length>1){
                for (int j = 0; j < xxbSplit.length; j++) {
                    if(j>0){
                        nginxXxbList.add(Constants.EXAM_INTERACTION_PATH+xxbSplit[j].substring(0,xxbSplit[j].indexOf("\'")));
                    }
                }
            }
            if(xxcSplit.length>1){
                for (int j = 0; j < xxcSplit.length; j++) {
                    if(j>0){
                        nginxXxcList.add(Constants.INTERACTION_UPLOAD_PATH+xxcSplit[j].substring(0,xxcSplit[j].indexOf("\'")-1));
                    }
                }
            }
            if(xxdSplit.length>1){
                for (int j = 0; j < xxdSplit.length; j++) {
                    if(j>0){
                        nginxXxdList.add(Constants.INTERACTION_ASSESS_PATH+xxdSplit[j].substring(0,xxdSplit[j].indexOf("\'")-1));
                    }
                }
            }

        }
        //根据path内容cp资源到服务器的mnt目录下(条件判断必须是linux环境下)
        //获取当前环境
        String osName = System.getProperty("os.name");
        String fileUploadPath;
        if (!osName.startsWith("Windows")) {
            //linux,创建文件夹汇总所有版本资源
            fileUploadPath= Constants.VERSION_PATH+"/";
            File uploadFile = new File(fileUploadPath);
            if (uploadFile.exists()) {
                //需要删除原先的resources资源文件夹
                String rmDirCommond = "rm -rf "+Constants.VERSION_PATH;
                Runtime.getRuntime().exec(rmDirCommond);
                log.info("删除原先的resources资源文件夹成功");
            }
            uploadFile.mkdirs();
            //复制fastdfs的新增资源
            if(fastdfsList.size()>0){
                FastDFSClientUtil fastDFSClientUtil = new FastDFSClientUtil();
                for (int i = 0; i < fastdfsList.size(); i++) {
                    String targetPath = fileUploadPath+"data"+fastdfsList.get(i);
                    String fastDirPath = targetPath.substring(0,targetPath.lastIndexOf("/"));
                    log.info("先创建文件目录:"+fastDirPath);
                    log.info("目标文件目录:"+targetPath);
                    File fastDir = new File(fastDirPath);
                    fastDir.mkdirs();
                    File fastFile = new File(targetPath);
                    fastFile.createNewFile();
                    int downFlag = fastDFSClientUtil.download_file(Constants.FASTDFS_PATH+fastdfsList.get(i),
                            new BufferedOutputStream(new FileOutputStream(targetPath)));
                    log.info("第"+(i+1)+"个文件的下载结果为:" + (downFlag == 0 ? "下载文件成功" : "下载文件失败"));
                }
            }
            //复制nginx下xxd、xxb、xxa、xxc下版本新增的交互动画资源(文件夹的资源形式)
            copyNginxFolder(nginxXxdList, fileUploadPath);
            copyNginxFolder(nginxXxbList, fileUploadPath);
            copyNginxFolder(nginxXxaList, fileUploadPath);
            copyNginxFolder(nginxXxcList, fileUploadPath);
        } else {
            throw new BusinessException("请发布linux系统后打包版本资源");
        }
        //打包shell脚本
        final Resource[] shellResources = new PathMatchingResourcePatternResolver().getResources(ResourceUtils.CLASSPATH_URL_PREFIX + Constants.RESOURCE_SHELL_PATH);
        String shellFolderPath;
        //linux,创建文件夹汇总所有版本资源
        shellFolderPath = Constants.VERSION_PATH + "/shell/";
        File shellFolderFile = new File(shellFolderPath);
        shellFolderFile.mkdirs();
        //复制shell文件
        log.info("复制shell文件...");
        for (int n = 0; n < shellResources.length; n++) {
            final Resource shellResource = shellResources[n];
            // 遍历文件内容
            StringBuffer shellScript = new StringBuffer();
            try(InputStreamReader isr = new InputStreamReader(shellResource.getInputStream(), StandardCharsets.UTF_8);
                BufferedReader bufferReader = new BufferedReader(isr)) {
                String tempString;
                while ((tempString = bufferReader.readLine()) != null) {
                    shellScript.append(tempString).append("\n");
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            String shellContent = shellScript.toString();
            String name = shellResource.getFilename();
            File dest = new File(shellFolderPath + name);
            dest.createNewFile();
            FileWriter fw=new FileWriter(dest,true);
            fw.write(shellContent);
            fw.close();
        }
        //压缩命令zip FileName.zip DirName压缩资源目录
        log.info("开始资源压缩打包...");
        FileOutputStream fos1 = new FileOutputStream(Constants.VERSION_NAME);
        ZipUtil.zip(Constants.VERSION_PATH, fos1);
        log.info("开始资源压缩打包成功");
        return Constants.VERSION_NAME;
    }


    /**
     * @Author: yz
     * @Date 19:14 2023/1/5
     * @Param [list, fileUploadPath]
     * @return 复制nginx目录下的资源文件夹
    */
    private static void copyNginxFolder(List<String> list, String fileUploadPath) throws Exception {
        if(list.size()>0){
            for (int i = 0; i < list.size(); i++) {
                String targetPath = fileUploadPath+"nginx/html/"+list.get(i);
                String sourcePath = Constants.NGINX_DOWNLOAD_PATH+list.get(i);
                log.info("nginx原资源文件路径:"+sourcePath);
                copyFolder(sourcePath,targetPath);
            }
        }
    }

    /**
     * @Author: yz
     * @Date 13:26 2023/1/5
     * @Param [file, list]
     * @return fastdfs解析文件封装函数
    */
    public static void fastdfsPackage(String content,List<String> list) throws IOException {
        String[] split = content.split("XXXXXX");
        for (int j = 0; j < split.length; j++) {
            if(j>0){
                list.add(split[j].substring(0,split[j].indexOf("\'")));
            }
        }
    }

   /**
     * @Author: yz
     * @Date 13:26 2023/1/5
     * 复制文件夹(使用缓冲字节流)
     * @param sourcePath 源文件夹路径
     * @param targetPath 目标文件夹路径
     */
    public static void copyFolder(String sourcePath,String targetPath) throws Exception{
        //源文件夹路径
        File sourceFile = new File(sourcePath);
        //目标文件夹路径
        File targetFile = new File(targetPath);

        if(!sourceFile.exists()){
            log.info("源文件夹不存在:"+sourcePath);
            return;
            //throw new Exception("文件夹不存在");
        }
        if(!sourceFile.isDirectory()){
            throw new Exception("源文件夹不是目录");
        }
        if(!targetFile.exists()){
            targetFile.mkdirs();
        }
        if(!targetFile.isDirectory()){
            throw new Exception("目标文件夹不是目录");
        }

        File[] files = sourceFile.listFiles();
        if(files == null || files.length == 0){
            return;
        }

        for(File file : files){
            //文件要移动的路径
            String movePath = targetFile+File.separator+file.getName();
            if(file.isDirectory()){
                //如果是目录则递归调用
                copyFolder(file.getAbsolutePath(),movePath);
            }else {
                //如果是文件则复制文件
                BufferedInputStream in = new BufferedInputStream(new FileInputStream(file));
                BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(movePath));

                byte[] b = new byte[1024];
                int temp = 0;
                while((temp = in.read(b)) != -1){
                    out.write(b,0,temp);
                }
                out.close();
                in.close();
            }
        }
    }

ZipUtil类:

@Slf4j
@RestController
public class ZipUtil {

    /**
     * 压缩文件夹到指定输出流中,可以是本地文件输出流,也可以是web响应下载流
     * @auther yz
     * @param srcDir       源文件夹
     * @param outputStream 压缩后文件的输出流
     * @throws IOException IO异常,抛出给调用者处理
     */
    public static void zip(String srcDir, OutputStream outputStream) throws IOException {
        try (
                BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(outputStream);
                ArchiveOutputStream out = new ZipArchiveOutputStream(bufferedOutputStream);
        ) {
            Path start = Paths.get(srcDir);
            Files.walkFileTree(start, new SimpleFileVisitor<Path>() {

                @Override
                public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
                    ArchiveEntry entry = new ZipArchiveEntry(dir.toFile(), start.relativize(dir).toString());
                    out.putArchiveEntry(entry);
                    out.closeArchiveEntry();
                    return super.preVisitDirectory(dir, attrs);
                }

                @Override
                public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                    try (
                            InputStream input = new FileInputStream(file.toFile())
                    ) {
                        ArchiveEntry entry = new ZipArchiveEntry(file.toFile(), start.relativize(file).toString());
                        out.putArchiveEntry(entry);
                        IOUtils.copy(input, out);
                        out.closeArchiveEntry();
                    }
                    return super.visitFile(file, attrs);
                }

            });

        }
    }

    /**
     * 解压zip文件到指定文件夹
     * @auther yz
     * @param zipFileName 源zip文件路径
     * @param destDir     解压后输出路径
     * @throws IOException IO异常,抛出给调用者处理
     */
    public static void unzip(String zipFileName, String destDir) throws IOException {
        try (
                InputStream inputStream = new FileInputStream(zipFileName);
        ) {
            unzip(inputStream, destDir);
        }

    }

    /**
     * 从输入流中获取zip文件,并解压到指定文件夹
     * @auther yz
     * @param inputStream zip文件输入流,可以是本地文件输入流,也可以是web请求上传流
     * @param destDir     解压后输出路径
     * @throws IOException IO异常,抛出给调用者处理
     */
    public static void unzip(InputStream inputStream, String destDir) throws IOException {
        try (
                BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);
                ArchiveInputStream in = new ZipArchiveInputStream(bufferedInputStream);
        ) {
            ArchiveEntry entry;
            while (Objects.nonNull(entry = in.getNextEntry())) {
                if (in.canReadEntryData(entry)) {
                    File file = Paths.get(destDir, entry.getName()).toFile();
                    if (entry.isDirectory()) {
                        if (!file.exists()) {
                            file.mkdirs();
                        }
                    } else {
                        try (
                                OutputStream out = new FileOutputStream(file);
                        ) {
                            IOUtils.copy(in, out);
                        }
                    }
                } else {
                    log.info(entry.getName());
                }
            }
        }

    }

}

 结果:


总结:

增量sql截取存储资源对应的path,找到资源服务器下对应的资源下载到服务器指定一个目录下,最后web端访问接口下载到本地


结束语: 

-----忧伤没有成本意识,激动没有判断能力,疲惫让人失去主见(旅游乱买,半夜买东西),结论:重要决策一定要平静的时候。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

热心码民阿振

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值