• SpringBoot静态资源访问控制和封装集成方案


    背景

    最近在着手公司框架优化及项目实际应用,原先方案是springboot+html前后端分离单独部署,后端人员兼职前端开发,后续产品线业务进行优化,面向企业使用部分由移动网站人员负责设计开发,内部配置后台管理还是由后端负责,随着框架不停迭代与使用的项目越来越多,项目升级框架变得十分麻烦,后端部分可以通过maven私服进行版本迭代,后台管理页面升级则需要进行各个项目拷贝,所以决定对框架进行整合,将后台管理页面与框架后端代码进行整合发布。

    结构设计

    • 框架打包后台管理相关标准资源及页面(框架public文件夹)
    • 项目使用框架,开发具体业务配置管理页面(项目static文件夹)
    • 项目需要个性化框架页面时,在项目static文件夹建立与框架同目录同名称资源文件进行覆盖,访问时优先级高于框架目录

    SpringBoot静态资源访问

    自定义访问路径

    自定义WebConfig实现WebMvcConfigurer,重写addResourceHandlers方法

    @Configuration
    public class WebConfig implements WebMvcConfigurer {
        @Value("${system.projectName}")
        private String projectName;
     
    
        /**
         * 添加静态资源文件,外部可以直接访问地址
         *
         * @param registry
         */
        @Override
        public void addResourceHandlers(ResourceHandlerRegistry registry) {
            //第一个方法设置访问路径前缀,第二个方法设置资源路径
            registry.addResourceHandler("/" + projectName + "/**").addResourceLocations("classpath:/static/","classpath:/public/","file:static/");
        }
    }
    

    图标与字体文件夹访问失败问题

    将静态文件拷贝到static/public/resource文件夹下访问时,图标与字体文件会进行过滤导致损坏,需要在pom文件中进行设置

     <build>
            <resources>
                <resource>
                    <filtering>true</filtering>
                    <directory>src/main/resources</directory>
                    <excludes>
                        <exclude>**/*.woff</exclude>
                        <exclude>**/*.ttf</exclude>
                        <exclude>**/*.ico</exclude>
                    </excludes>
                </resource>
                <resource>
                    <filtering>false</filtering>
                    <directory>src/main/resources</directory>
                    <includes>
                        <include>**/*.woff</include>
                        <include>**/*.ttf</include>
                        <include>**/*.ico</include>
                    </includes>
                </resource>
            </resources>
     </build>
    

    自定义欢迎页面

    在对静态内目录设置自定义访问路径替换原有的/**后,无法找到目录下的index页面,需要建立拦截器手动进行判断,效果为访问http://localhost:port/projectName 会自动跳转到 http://localhost:port/projectName/index.html

    @Component
    public class PageRedirectInterceptor implements HandlerInterceptor {
        @Value("${system.projectName}")
        private String projectName;
    
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            String requestURL = request.getRequestURL().toString();
            String scheme = request.getScheme();
            String servaerName = request.getServerName();
            int port = request.getServerPort();
            String rootPageURL = scheme + ":" + "//" + servaerName + ":" + port + "/" + projectName;
            if (requestURL.equals(rootPageURL)) {
                response.sendRedirect(request.getContextPath() + "/"+projectName + "/index.html");
                return false;
            }
            return true;
        }
    }
    

    自定义页面图标

    在对静态内目录设置自定义访问路径替换原有的/**后,无法找到目录下的favcion.ico图标,需要在页面引用统一js统一设置,同时需要在配置文件中关闭默认图标,替换spring的小叶子

    spring:
      mvc:
        favicon:
          enabled: false
    
    function GetRootPath() {
        var loc = window.location,
            host = loc.hostname,
            protocol = loc.protocol,
            port = loc.port ? (':' + loc.port) : '';
        var path = location.pathname;
    
        if (path.indexOf('/') === 0) {
            path = path.substring(1);
        }
    
        var mypath = '/' + path.split('/')[0];
        path = (mypath != undefined ? mypath : ('/' + loc.pathname.split('/')[1])) + '/';
    
        var rootPath = protocol + '//' + host + port + path;
        return rootPath;
    }
    
    var iconurl = GetRootPath()+"favicon.ico"
    document.write('<link rel="shortcut icon" href= ' + iconurl + '  rel="external nofollow" rel="external nofollow" ></link>');
    

    项目访问框架静态资源

    框架静态资源文件获取

    项目启动时,因为是引用框架的jar包,我们需要先找到指定jar包,再将jar包进行解压,找到对应目录将资源拷贝到我们需要的地方便于访问

    扫描jar包

     public static void copyFrameStaticFile() {
            String packageName = "com.haopan.frame";
            // 获取包的名字 并进行替换
            String packageDirName = packageName.replace('.', '/');
            // 定义一个枚举的集合 并进行循环来处理这个目录下的things
            Enumeration<URL> dirs;
            try {
                dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName);
                // 循环迭代下去
                while (dirs.hasMoreElements()) {
                    // 获取下一个元素
                    URL url = dirs.nextElement();
                    // 得到协议的名称
                    String protocol = url.getProtocol();
                    if ("jar".equals(protocol)) {
                        // 如果是jar包文件
                        // 定义一个JarFile
                        JarFile jar;
                        try {
                            // 获取jar
                            jar = ((JarURLConnection) url.openConnection()).getJarFile();
                            String templateDecompressPath = "tempfiles/decompress/" + CommonUtil.getNewGuid() + "/";
                            File targetFile = new File(templateDecompressPath);
                            if (!targetFile.exists()) {
                                targetFile.mkdirs();
                            }
                            decompressJarFile(jar, templateDecompressPath);
                            String frameStaticPath = templateDecompressPath + "public/";
                            File frameStaticFile = new File(frameStaticPath);
                            if (frameStaticFile.exists()) {
                                String copyTargetPath = "static/";
                                File copyTargetFolder = new File(copyTargetPath);
                                if (copyTargetFolder.exists()) {
                                    FileUtil.deleteDirectory(copyTargetPath);
                                }
                                copyTargetFolder.mkdirs();
                                FileUtil.copyFileFolder(frameStaticPath, copyTargetPath);
                            }
                            FileUtil.deleteDirectory(templateDecompressPath);
                            System.out.println("框架静态文件复制完毕!");
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    

    解压jar包

    对JarFile中的JarEntry对象进行遍历,判断是文件还是目录分类处理

    public static synchronized void decompressJarFile(JarFile jf,String outputPath){
            if (!outputPath.endsWith(File.separator)) {
                outputPath += File.separator;
            }
            File dir = new File(outputPath);
            if (!dir.exists()) {
                dir.mkdirs();
            }
            try{
                for (Enumeration<JarEntry> e = jf.entries(); e.hasMoreElements();) {
                    JarEntry je = (JarEntry) e.nextElement();
                    String outFileName = outputPath + je.getName();
                    File f = new File(outFileName);
                    if(je.isDirectory()){
                        if(!f.exists()){
                            f.mkdirs();
                        }
                    }else{
                        File pf = f.getParentFile();
                        if(!pf.exists()){
                            pf.mkdirs();
                        }
                        InputStream in = jf.getInputStream(je);
                        OutputStream out = new BufferedOutputStream(
                                new FileOutputStream(f));
                        byte[] buffer = new byte[2048];
                        int nBytes = 0;
                        while ((nBytes = in.read(buffer)) > 0) {
                            out.write(buffer, 0, nBytes);
                        }
                        out.flush();
                        out.close();
                        in.close();
                    }
                }
            }catch(Exception e){
                System.out.println("解压"+jf.getName()+"出错---"+e.getMessage());
            }finally{
                if(jf!=null){
                    try {
                        jf.close();
                        File jar = new File(jf.getName());
                        if(jar.exists()){
                            jar.delete();
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    

    拷贝目录到指定位置

    public class FileUtil {
        private static void copy(String f1, String f2) throws IOException {
            File file1=new File(f1);
            /*     File file2=new File(f2);*/
    
            File[] flist=file1.listFiles();
            for (File f : flist) {
                if(f.isFile()){
                    copyFile(f.getPath(),f2+"/"+f.getName()); //调用复制文件的方法
                    //System.out.println("原路径["+f.getPath()+"] 被复制路径["+f2+"/"+f.getName()+"]");
                }else if(f.isDirectory()){
                    copyFileFolder(f.getPath(),f2+"/"+f.getName()); //调用复制文件夹的方法
                    //System.out.println("原路径["+f.getPath()+"] 被复制路径["+f2+"/"+f.getName()+"]");
                }
            }
        }
    
        /**
         * 复制文件夹
         * @throws IOException
         */
        public static void copyFileFolder(String sourceFolderPath,String targetFolderPath) throws IOException {
            //创建文件夹
            File file=new File(targetFolderPath);
            if(!file.exists()){
                file.mkdirs();
            }
            copy(sourceFolderPath,targetFolderPath);
        }
    
        /**
         * 复制文件
         * @throws IOException
         */
        public static void copyFile(String sourceFilePath, String tagretFilePath) throws IOException {
            try {
                InputStream in = new FileInputStream(sourceFilePath);
                OutputStream out = new FileOutputStream(tagretFilePath);
                byte[] buffer = new byte[2048];
                int nBytes = 0;
                while ((nBytes = in.read(buffer)) > 0) {
                    out.write(buffer, 0, nBytes);
                }
                out.flush();
                out.close();
                in.close();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
        }
    
        public static boolean delete(String fileName) {
            File file =new File(fileName);
            if (!file.exists()) {
                //System.out.println("删除文件失败:" + fileName +"不存在!");
                return false;
            }else {
                if (file.isFile())
                    return deleteFile(fileName);
                else
                    return deleteDirectory(fileName);
            }
        }
    
        /**
         * 删除单个文件
         *
         * @param fileName:要删除的文件的文件名
         * @return 单个文件删除成功返回true,否则返回false
         */
        public static boolean deleteFile(String fileName) {
            File file =new File(fileName);
            // 如果文件路径所对应的文件存在,并且是一个文件,则直接删除
            if (file.exists() && file.isFile()) {
                if (file.delete()) {
                    //System.out.println("删除单个文件" + fileName +"成功!");
                    return true;
                }else {
                    //System.out.println("删除单个文件" + fileName +"失败!");
                    return false;
                }
            }else {
                //System.out.println("删除单个文件失败:" + fileName +"不存在!");
                return false;
            }
        }
    
        /**
         * 删除目录及目录下的文件
         *
         * @param dir:要删除的目录的文件路径
         * @return 目录删除成功返回true,否则返回false
         */
        public static boolean deleteDirectory(String dir) {
            // 如果dir不以文件分隔符结尾,自动添加文件分隔符
            if (!dir.endsWith(File.separator))
                dir = dir + File.separator;
            File dirFile =new File(dir);
            // 如果dir对应的文件不存在,或者不是一个目录,则退出
            if ((!dirFile.exists()) || (!dirFile.isDirectory())) {
                System.out.println("删除目录失败:" + dir +"不存在!");
                return false;
            }
            boolean flag =true;
            // 删除文件夹中的所有文件包括子目录
            File[] files = dirFile.listFiles();
            for (int i =0; i < files.length; i++) {
                // 删除子文件
                if (files[i].isFile()) {
                    flag = deleteFile(files[i].getAbsolutePath());
                    if (!flag)
                        break;
                }
                // 删除子目录
                else if (files[i].isDirectory()) {
                    flag = deleteDirectory(files[i].getAbsolutePath());
                    if (!flag)
                        break;
                }
            }
            if (!flag) {
                //System.out.println("删除目录失败!");
                return false;
            }
            // 删除当前目录
            if (dirFile.delete()) {
                //System.out.println("删除目录" + dir +"成功!");
                return true;
            }else {
                return false;
            }
        }
    
    }
    

    外部静态资源访问与优先级设置

    设置yml文件中的static-locations配置项,多个使用,隔开,同时指定顺序为访问的优先级

    spring:
      resources:
        static-locations: classpath:static/,classpath:public/,file:static/
    

    最终目录结构图如下,框架部分完全是项目启动时自动解压拷贝的,项目部分则是由具体项目进行开发,项目部分也可以很方便的进行框架部分功能重构,例如登录页,主页面修改等,本方式支持jar包和war包两种打包方式

  • 相关阅读:
    nginx优化配置
    mysql查看变量/配置文件位置
    关于ubuntu的ssh远程登录的问题
    ubuntu镜像下载地址
    百度地图标注地点
    Yii常用方法
    python_将一组数据展示成直方图(以list为例)
    opencv_形态学结构化元素对形态学图像处理的影响
    C语言学习_从VC++6.0开始
    SVM原理(1)
  • 原文地址:https://www.cnblogs.com/yanpeng19940119/p/15084899.html
Copyright © 2020-2023  润新知