使用Java往Geoserver发布tif图层和shp图层

news2025/1/11 16:01:56
1. Maven依赖

栅格文件对应Tif文件 (即: 栅格就是tif)

矢量文件对应shp文件(即: 矢量就是shp)

注: 有的依赖可能在中央仓库及一些镜像仓库找不到需要手动指定仓库, 在依赖最下方

        <!--  中文转拼音工具类 -->
        <dependency>
            <groupId>com.belerweb</groupId>
            <artifactId>pinyin4j</artifactId>
            <version>2.5.0</version>
        </dependency>
        <!--  工具类 -->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.3</version>
        </dependency>
        <!--  geoserver管理工具类 -->
        <dependency>
            <groupId>it.geosolutions</groupId>
            <artifactId>geoserver-manager</artifactId>
            <version>1.7.0</version>
        </dependency>
        <!--  geojson工具类 -->
        <dependency>
            <groupId>org.geotools</groupId>
            <artifactId>gt-geojson</artifactId>
            <version>25.3.01</version>
        </dependency>
        <!--  geoserver数据库工具类 -->
        <dependency>
            <groupId>org.geotools</groupId>
            <artifactId>gt-epsg-hsql</artifactId>
            <version>24.2</version>
        </dependency>
    
    <!-- 中央仓库没有 gt-geotiff, gt-shapefile 指定新的仓库 -->
    <repositories>
        <repository>
            <id>GeoSolutions</id>
            <url>http://maven.geo-solutions.it/</url>
        </repository>
    </repositories>
2. GeoServerUtil工具类

工具类主要方法如下:

  1. 发布tif
  2. 发布shp
  3. 获取栅格bbox (Bounding Box) 经纬度边界
  4. 获取矢量bbox (Bounding Box) 经纬度边界

注: 其中使用到了pinyin4j主要防止文件命名是中文, 使用改工具类可以使中文转换为拼音 默认非大小驼峰命名, 可根据需要自行更改. '你好'->'nihao'

import cn.hutool.core.io.FileUtil;
import cn.hutool.extra.pinyin.engine.PinyinFactory;
import it.geosolutions.geoserver.rest.GeoServerRESTManager;
import it.geosolutions.geoserver.rest.GeoServerRESTPublisher;
import it.geosolutions.geoserver.rest.GeoServerRESTReader;
import it.geosolutions.geoserver.rest.decoder.*;
import it.geosolutions.geoserver.rest.encoder.GSLayerGroupEncoder;
import it.geosolutions.geoserver.rest.encoder.GSResourceEncoder;
import it.geosolutions.geoserver.rest.encoder.datastore.GSShapefileDatastoreEncoder;
import org.apache.commons.httpclient.NameValuePair;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileNotFoundException;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.List;

/**
 * @author Cookie
 * @date 2024/8/22
 * @desc GeoServerUtil
 */
public class GeoServerUtil {

    private static final Logger logger = LoggerFactory.getLogger(GeoServerUtil.class);

    public static GeoServerRESTManager geoServerRESTManager;

    public static GeoServerRESTManager buildManager(String url, String username, String password) {
        try {
            if (geoServerRESTManager == null)
                geoServerRESTManager = new GeoServerRESTManager(new URL(url), username, password);
            return geoServerRESTManager;
        } catch (MalformedURLException e) {
            logger.error("GeoServer连接失败,请检查连接地址是否正确");
            logger.error(e.getMessage(), e);
            return null;
        }
    }

    private String url;
    private String username;
    private String password;

    public GeoServerUtil(String url, String username, String password) {
        this.url = url;
        this.username = username;
        this.password = password;
    }

    public GeoServerUtil() {

    }

    /**
     * 发布shp文件
     * 要求: 压缩包名称和压缩包内所有的文件名的名字相同并且只有一级
     * 示例:
     * shp_demo.zip:
     * * * shp_demo.dbf
     * * * shp_demo.fix
     * * * shp_demo.prj
     * * * shp_demo.shp
     * * * shp_demo.shx
     *
     * @param zipFile    shp压缩文件路径
     * @param workspace  工作空间
     * @param store      存储仓库
     * @param layerName  图层名
     * @param layerGroup 图层组
     * @param layerStyle 图层样式
     */
    public boolean publishShp(String zipFile, String workspace, String store, String layerName, String layerGroup, String layerStyle) {

        if (verifyFileExists(new File(zipFile))) return false;
        if (StringUtils.isEmpty(layerStyle))
            layerStyle = "polygon";

        GeoServerRESTManager geoServerRESTManager = buildManager(this.url, this.username, this.password);
        if (geoServerRESTManager == null)
            throw new RuntimeException("GeoServer连接失败");
        GeoServerRESTPublisher publisher = geoServerRESTManager.getPublisher();
        //创建工作空间
        List<String> workspaceNames = geoServerRESTManager.getReader().getWorkspaceNames();
        logger.info("存在的workspace:{}", workspaceNames);
        if (!workspaceNames.contains(workspace)) {
            if (!publisher.createWorkspace(workspace)) {
                return false;
            }
        }
        //创建存储
        RESTDataStore datastore = geoServerRESTManager.getReader().getDatastore(workspace, store);
        try {
            if (datastore == null) {
                String str = File.separator;
                if (str.equals("\\")) {
                    str = "\\\\";
                }
                String[] split = zipFile.split(str);
                String name = split[split.length - 1];
                name = name.substring(0, name.lastIndexOf("."));
                String urlDatastore = "file:data/" + workspace + "/" + name + "/" + name + ".shp";
                GSShapefileDatastoreEncoder storeEncoder = new GSShapefileDatastoreEncoder(store, new URL(urlDatastore));
                storeEncoder.setCharset(StandardCharsets.UTF_8);
                if (!geoServerRESTManager.getStoreManager().create(workspace, storeEncoder)) {
                    return false;
                }
            }
            //发布图层

            if (!publisher.publishShp(workspace, store, new NameValuePair[]{new NameValuePair("charset", "utf-8")}, layerName,
                    GeoServerRESTPublisher.UploadMethod.FILE, new File(zipFile).toURI(), "EPSG:4326", layerStyle)) {
                return false;
            }
        } catch (FileNotFoundException e) {
            logger.error("文件不存在:{}", e.getMessage());
            throw new RuntimeException("文件不存在");
        } catch (MalformedURLException e) {
            logger.error("URL格式错误:{}", e.getMessage());
            throw new RuntimeException("URL格式错误");
        }

        if (layerGroup != null) {
            //加入图层组
            RESTLayerGroup restLayerGroup = geoServerRESTManager.getReader().getLayerGroup(workspace, layerGroup);
            GSLayerGroupEncoder gsLayerGroupEncoder = new GSLayerGroupEncoder();
            gsLayerGroupEncoder.addLayer(layerName);
            if (restLayerGroup == null) {
                geoServerRESTManager.getPublisher().createLayerGroup(workspace, layerGroup, gsLayerGroupEncoder);
            } else {
                geoServerRESTManager.getPublisher().configureLayerGroup(workspace, layerGroup, gsLayerGroupEncoder);
            }
        }
        return true;
    }

    /**
     * 发布shp文件
     *
     * @param zipFile    shp压缩文件路径
     * @param workspace  工作空间
     * @param layerGroup 图层组
     * @param layerStyle 图层样式
     */
    public boolean publishShp(String zipFile, String workspace, String layerGroup, String layerStyle) {
        File file = new File(zipFile);
        if (verifyFileExists(file)) return false;

        String fileName = getFileName(file);
        return publishShp(zipFile, workspace, fileName, fileName, layerGroup, layerStyle);
    }

    /**
     * 发布shp文件
     *
     * @param zipFile    shp压缩文件路径
     * @param workspace  工作空间
     * @param layerGroup 图层组
     */
    public boolean publishShp(String zipFile, String workspace, String layerGroup) {
        return publishShp(zipFile, workspace, layerGroup, null);
    }

    public boolean deleteStore(String workspace, String storeName) {
        GeoServerRESTManager geoServerRESTManager = buildManager(this.url, this.username, this.password);
        if (geoServerRESTManager == null)
            throw new RuntimeException("GeoServer连接失败");
        GeoServerRESTPublisher publisher = geoServerRESTManager.getPublisher();
        return publisher.removeDatastore(workspace, storeName, false);
    }

    /**
     * 获取栅格bbox
     *
     * @param workspace 工作空间
     * @param store     存储仓库
     * @param layerName 图层名称
     * @return [minX, minY, maxX, maxY]
     */
    public double[] getTiffBbox(String workspace, String store, String layerName) {
        GeoServerRESTManager restManager = buildManager(this.url, this.username, this.password);
        if (restManager == null)
            throw new RuntimeException("GeoServer连接失败");
        GeoServerRESTReader reader = restManager.getReader();

        RESTCoverage coverage = reader.getCoverage(workspace, store, layerName);
        RESTBoundingBox nativeBoundingBox = coverage.getNativeBoundingBox();

        return new double[]{nativeBoundingBox.getMinX(), nativeBoundingBox.getMinY(), nativeBoundingBox.getMaxX(), nativeBoundingBox.getMaxY()};
    }

    /**
     * 获取栅格bbox(Bounding Box)
     * store和layerName相同
     *
     * @param workspace 工作空间
     * @param layerName 图层名称
     * @return [minX, minY, maxX, maxY]
     */
    public double[] getTiffBbox(String workspace, String layerName) {
        return getTiffBbox(workspace, layerName, layerName);
    }

    /**
     * 获取矢量bbox(Bounding Box)
     * 经纬度边框
     *
     * @param workspace 工作空间
     * @param layerName 图层名称
     * @return min(x, y) max(x,y)
     */
    public double[] getShpBbox(String workspace, String layerName) {
        GeoServerRESTReader reader = geoServerRESTManager.getReader();
        RESTLayer layer = reader.getLayer(workspace, layerName);
        RESTFeatureType featureType = reader.getFeatureType(layer);
        RESTBoundingBox nativeBoundingBox = featureType.getNativeBoundingBox();
        return new double[]{nativeBoundingBox.getMinX(), nativeBoundingBox.getMinY(), nativeBoundingBox.getMaxX(), nativeBoundingBox.getMaxY()};
    }

    /**
     * 发布tif文件
     *
     * @param filePath  文件路径
     * @param workspace 工作空间
     * @param styleName 样式名称 (样式不存在默认为空)
     * @param store     存储仓库
     * @param layerName 图层名称
     * @return true/false
     */
    public boolean publishTiff(String filePath, String workspace, String styleName, String store, String layerName) {
        if (verifyFileExists(new File(filePath))) return false;
        if (StringUtils.isEmpty(styleName))
            // 默认样式名称
            styleName = "raster";
        GeoServerRESTManager geoServerRESTManager = buildManager(this.url, this.username, this.password);
        if (geoServerRESTManager == null)
            throw new RuntimeException("GeoServer连接失败");
        GeoServerRESTPublisher publisher = geoServerRESTManager.getPublisher();
        //创建工作空间
        List<String> workspaceNames = geoServerRESTManager.getReader().getWorkspaceNames();
        if (!workspaceNames.contains(workspace) && !publisher.createWorkspace(workspace))
            return false;
        try {
            return publisher.publishGeoTIFF(workspace, store, layerName, new File(filePath), "EPSG:4326", GSResourceEncoder.ProjectionPolicy.NONE, styleName, null);
        } catch (Exception e) {
            logger.error("发布tif文件失败: 异常信息{}", e.getMessage());
            return false;
        }
    }

    /**
     * 发布tiff文件
     * 使用默认coverageName和store
     *
     * @param filePath  文件路径样式
     * @param workspace 工作空间
     * @param styleName 样式名称
     * @return 是否发布成功
     */
    public boolean publishTiff(String filePath, String workspace, String styleName) {
        File file = new File(filePath);
        if (verifyFileExists(file)) return false;
        String fileName = getFileName(file);
        return publishTiff(filePath, styleName, fileName, workspace, fileName);
    }

    /**
     * 发布tiff文件
     * 使用默认coverageName和store
     *
     * @param filePath  文件路径样式
     * @param workspace 工作空间
     * @return 是否发布成功
     */
    public boolean publishTiff(String filePath, String workspace) {
        File file = new File(filePath);

        if (verifyFileExists(file)) return false;

        String fileName = getFileName(file);
        return publishTiff(filePath, "", fileName, workspace, fileName);
    }

    /**
     * 发布多个TIFF文件
     * 注: 存储仓库默认使用文件名
     *
     * @param localFolder 本地文件夹路径
     * @param workspace   命令空间
     * @param styleName   矢量或栅格图层使用的样式(不传使用默认样式)
     */
    public void publishTiffs(String localFolder, String workspace, String styleName) {

        List<File> files = FileUtil.loopFiles(localFolder, file -> file.getName().endsWith(".tif"));

        for (File file : files) {
            String fileName = getFileName(file);
            System.err.println("文件路径:" + file.getAbsolutePath());
            // 使用默认样式
            boolean publish = false;
            try {
                publish = publishTiff(file.getPath(), workspace, styleName, fileName, fileName);
            } catch (Exception e) {
                System.err.println("发布失败: " + e.getMessage());
            }
            System.out.println(file.getName() + "发布结果:" + (publish ? "成功" : "失败"));
        }
    }

    /**
     * 发布多个TIFF文件
     * 注: 存储仓库默认使用文件名
     *
     * @param localFolder 本地文件夹路径
     * @param workspace   命令空间
     */
    public void publishTiffs(String localFolder, String workspace) {
        publishTiffs(localFolder, workspace, null);
    }

    /**
     * 获取文件名
     * 注: 支持中文转换为拼音
     *
     * @param file FILE
     * @return 文件名
     */
    private static String getFileName(File file) {
        String[] split = file.getName().split("\\.");
        // 防止使用中文名称通过工具类转换 你好-> nihao
        return PinyinFactory.get().getPinyin(split[0], "");
    }

    /**
     * 校验文件是否存在
     *
     * @param file file
     * @return
     */
    private static boolean verifyFileExists(File file) {
        if (!file.exists()) {
            logger.error("文件不存在");
            return true;
        }
        return false;
    }

}
3. 工具类测试

其中有一些重载方法可根据需要自行修改

/**
 * @Author Cookie
 * @Date 2024/8/22
 * @Desc
 */
public class GeoserverTest {

    private GeoServerUtil geoServerUtil;

    @Before
    public void initGeoServerUtil() {
        geoServerUtil = new GeoServerUtil("http://localhost:8888/geoserver", "admin", "geoserver");
    }

    /**
     * 获取栅格bbox(Bounding Box)测试
     */
    @Test
    public void getTiffBboxTest() {
        // 这里重载了一个方法,手动指定仓库名称. 默认仓库名等于文件名
        double[] shpBbox = geoServerUtil.getTiffBbox("15000", "20240819_yumi");
        System.out.println(Arrays.toString(shpBbox));
        // [minX, minY, maxX, maxY]
        // [112.92316424712779, 47.753672584652726, 113.22898940248196, 48.01270170916157]
    }

    /**
     * 发布tif文件
     */
    @Test
    public void publishTiffTest() {
        // 重载方法一: 默认仓库名等于文件名
//        geoServerUtil.publishTiff("C:\\Users\\Administrator\\Desktop\\tif\\2024_dadou.tif", "15000");
        // 重载方法二: 指定图层样式
//        geoServerUtil.publishTiff("C:\\Users\\Administrator\\Desktop\\tif\\2024_dadou.tif", "15000", "raster-dadou");
        // 重载方法三: 指定图层名称和仓库名称
        geoServerUtil.publishTiff("C:\\Users\\Administrator\\Desktop\\tif\\20240819_dadou.tif", "15000", "raster-dadou-out", "dadou_store", "dadou1");
    }

    /**
     * 批量发布tif文件
     */
    @Test
    public void publishTiffsTest() {
        geoServerUtil.publishTiffs("C:\\Users\\Administrator\\Desktop\\tif", "15000");
    }

    /**
     * 发布矢量图层 shp
     */
    @Test
    public void publishShpTest() throws Exception {
        // 重载方法一: 默认图层名称等于文件名
        geoServerUtil.publishShp("C:\\Users\\Administrator\\Desktop\\neihuang_2800.zip", "15000", "neihuang");
        // 重载方法二: 指定样式
        geoServerUtil.publishShp("C:\\Users\\Administrator\\Desktop\\neihuang_2800.zip", "15000", "neihuang", "polygon");
        // 重载方法三: 全指定
        geoServerUtil.publishShp("C:\\Users\\Administrator\\Desktop\\neihuang_2800.zip", "15000", "neihuang_store", "neihuang_2024", "neihuang", "polygon");
    }

4. 结果查看

如果shp文件点击图层预览OpenLayers后是直接下载本地, 检查一下图层的样式是否正确

1. tif文件查看

在geoserver中的图层预览图层中都可以看到这两个图层, 点击OpenLayers可查看图层,

在这里插入图片描述

给图层绑定一下样式得到如下效果:

到这里基本就完成了图层的发布. 图层打开后的链接, 换成公网可以访问的链接就可以给前端的同事使用了.

关于样式的发布可以参考官方文档 geoserverSLD造型

2.shp文件查看

示例如下:
在这里插入图片描述

如果依赖一直拉不下来可以试试下面的方式清理一下本地maven仓库中的 .lastUpdated文件重新下载

maven快速清除lastUpdated文件

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2065009.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

指针的学习和理解

初级 1、指针的概念 在64位操作系统中&#xff0c;不管什么类型的指针都占8个字节 int a1; int* p&a;//p就是一个整型的指针&#xff0c;保存了a的地址2、指针和变量 int* p&a;* p100; // 等价于a100p //p&a*有两种定义&#xff1a; 定义的时候&#xff08;前…

【工具类】Java优雅的将XML转为JSON格式、XML转JSON

Java优雅的将XML转为JSON格式、XML转JSON 1. 导入依赖1.1 Maven使用1.2 Gradle使用 2. 代码编写3.运行示例 1. 导入依赖 1.1 Maven使用 <dependency><groupId>org.dom4j</groupId><artifactId>dom4j</artifactId><version>2.1.3</vers…

TCP连接过程

文章目录 TCP连接过程 附录TCP报文中关键术语字段 后面再完整出理论、出实战、出总结 TCP连接过程 三次握手&#xff08;Three-Way Handshake&#xff09;过程。 TCP抓包结果分析&#xff1a; step1&#xff1a;Client1客户端--->Server1服务器发送SYN&#xff08;同步…

【C++二分查找 前缀和】1658. 将 x 减到 0 的最小操作数

本文涉及的基础知识点 C二分查找 C算法&#xff1a;前缀和、前缀乘积、前缀异或的原理、源码及测试用例 包括课程视频 LeetCode1658. 将 x 减到 0 的最小操作数 给你一个整数数组 nums 和一个整数 x 。每一次操作时&#xff0c;你应当移除数组 nums 最左边或最右边的元素&am…

MambaCSR: 使用SSM的双交错扫描压缩图像超分辨率

MambaCSR: Dual-Interleaved Scanning for Compressed Image Super-Resolution With SSMs 2408.11758 (arxiv.org) GitHub - renyulin-f/MambaCSR: The code source of MambaCSR 摘要 本文提出了MambaCSR&#xff0c;这是一个基于Mamba的简单但有效的框架&#xff0c;用于解决…

ffmpeg读取时长、读取视频格式

ffmpeg读取时长、读取视频格式 ffmpeg读取时长ffmpeg读取视频格式 ffmpeg读取时长 命令命令介绍具体用法ffmpeg -i查看视频时长ffmpeg -i 视频链接 or 视频路径 2>&1 | grep Duration ffmpeg读取视频格式 命令命令介绍具体用法ffmpeg -i查看视频时长ffmpeg -i 视频链接…

集合及数据结构第八节(下)———— 队列(Queue)、队列的模拟实现和练习

系列文章目录 集合及数据结构第八节&#xff08;下&#xff09;———— 队列(Queue)、队列的模拟实现和练习 队列(Queue)、队列的模拟实现和练习 队列的概念队列的使用队列模拟实现循环队列双端队列练习题 文章目录 系列文章目录集合及数据结构第八节&#xff08;下&#x…

Chainlit接入DifyAI知识库接口快速实现自定义用户聊天界面

前言 由于dify只提供了一个分享用的网页应用&#xff0c;网页访问地址没法自定义&#xff0c;虽然可以接入NextWeb/ChatGPT web/open webui等开源应用。但是如果我们想直接给客户应用&#xff0c;还需要客户去设置配置&#xff0c;里面还有很多我们不想展示给客户的东西怎么办…

【C语言】文件操作 (详细!!)

1、为什么使用文件 使用文件的原因&#xff1a;使用文件主要是为了在程序的执行过程中保存、读取和交换数据。文件提供了一种持久化存储数据的方式&#xff0c;使得程序在关闭后&#xff0c;数据不会丢失&#xff0c;可以被其他程序或后续的程序执行周期重新读取和处理。 1.0 什…

实验2-1-3 输出三角形

本题要求编写程序&#xff0c;输出指定的由“*”组成的三角图案。 **输入格式&#xff1a; 本题无输入**输出格式&#xff1a; 按照下列格式输出由“*”组成的三角图案。 **** *** ** *程序: #define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> int main() {int i…

leetcode 49 字母异位分词

正文 基础解法 首先&#xff0c;我们创建一个字典对象&#xff0c;然后遍历整个字符串列表&#xff0c;并且使用 sorted() 函数对字符串列表进行排序&#xff0c;所有的异位分词经过排序后它们的组成和顺序会趋于一致。但是需要注意的是 sorted 对字符串进行排序后会变成一个由…

基于element-ui 日期选择器el-date-picker, 即对日期做区间限制

需求&#xff1a; 有时候需求会让我们对日期选择器做限制&#xff0c;即控制最多可跨越多少个月份&#xff0c;其中涉及到不同年份该如何计算。 HTML&#xff1a; <el-date-pickerv-model"timePeriod"type"monthrange"value-format"yyyyMM"…

Linux系统之部署俄罗斯方块网页小游戏(三)

Linux系统之部署俄罗斯方块网页小游戏(三) 一、小游戏介绍1.1 小游戏简介1.2 项目预览二、本次实践介绍2.1 本地环境规划2.2 本次实践介绍三、检查本地环境3.1 检查系统版本3.2 检查系统内核版本3.3 检查软件源四、安装Apache24.1 安装Apache2软件4.2 启动apache2服务4.3 查看…

【CANoe使用大全】——cdd导入CANoe流程详解

&#x1f64b;‍♂️【CANoe使用大全】系列&#x1f481;‍♂️点击跳转 文章目录 1.1.CDD导入1.1 CDD文件导入流程 2. CDD文件导后配置2.1.协议配置2.2.寻址方式配置2.3.0x27 解密DLL导入2.4.诊断ID配置 3.导入效果4.CDD操作台使用4.1.指令发送 5.Fault Memory5.1 0x19 045.2…

解释图像的边缘检测算法中的Canny算法

Canny 算法是图像处理领域中一种经典的边缘检测方法&#xff0c;由 John F. Canny 在 1986 年提出。Canny 算法以其高效、可靠的边缘检测效果在图像处理和计算机视觉领域广泛应用。它具有良好的噪声抑制能力、精确的边缘定位能力以及单像素宽度的边缘输出特性。 Canny 边缘检测…

TIM输出比较之PWM驱动LED呼吸灯应用案例

文章目录 前言一、应用案例演示二、电路接线图三、应用案例代码四、应用案例分析4.1 基本思路4.2 相关库函数介绍4.3 初始化PWM模块4.3.1 RCC开启时钟4.3.2 配置时基单元4.3.3 配置输出比较单元4.3.4 配置GPIO4.3.5 运行控制 4.4 PWM输出模块4.5 主程序 前言 提示&#xff1a;…

无人机培训与装配维修技术详解

一、无人机基础理论 无人机&#xff0c;即无人驾驶航空器&#xff0c;凭借其灵活性、高效性和广泛应用性&#xff0c;已成为现代科技领域的热点之一。在学习无人机培训与装配维修技术之前&#xff0c;掌握无人机的基础理论是必不可少的。这包括但不限于&#xff1a; 1. 无人机…

Alpaca 汉化版 v2.9.3 — 免费 PS 智能 AI 插件

Alpaca是一款免费的PS智能AI插件&#xff0c;包含了6大AI功能&#xff0c;包括提示词生图、图像转绘画风格、生成式填充、文本转图像、计算图像模型、提高图像分辨率。汉化版本安装简单&#xff0c;只需解压到PhotoShop安装目录\Plug-ins文件夹即可。安装启动PhotoShop - 增效工…

基于Springboot和BS架构的宠物健康咨询系统pf

TOC springboot509基于Springboot和BS架构的宠物健康咨询系统pf 第一章 课题背景及研究内容 1.1 课题背景 信息数据从传统到当代&#xff0c;是一直在变革当中&#xff0c;突如其来的互联网让传统的信息管理看到了革命性的曙光&#xff0c;因为传统信息管理从时效性&#x…

前端本地代理配置方式

Whistle 介绍 Whistle 是一个基于 Node.js 的跨平台 Web 调试工具。允许捕获、查看和修改 HTTP/HTTPS 网络请求。通过使用 Whistle&#xff0c;可以轻松地进行接口代理、抓包、模拟数据、修改请求和响应等操作&#xff0c;以便在前端开发中调试网络请求。 Proxy SwitchyOmega…