MINIO无公网访问地址下OSS的配置

在实际项目中,会存在MINIO外网不能访问,只能通过应用的域名(IP)进行转发才能访问的情况。该篇文章主要是为解决这种场景。

PS:如果MINIO外网可以直接访问,正常配置MINIO的OSS配置即可。

阅读之前

  • 了解MINIO对endpoint的限制, As per S3 specification, path in the endpoint is not supported. 即MINIO的请求地址不能带path.

详细参考:https://github.com/minio/minio-java/issues/1476
MINIO无公网访问地址下OSS的配置

解决办法

  • 项目中的MINIO地址配置一个可以访问的外网地址; 在网络层(NGINX)中配置该外网地址到MINIO的内网地址;
  • 因MINIO对endpoint的限制只能是 ip+port,即不能包含path, 也即不能通过配置path的方式进行转发;需要给一个IP(域名)+ 端口的 外部地址。

详细配置步骤

项目中OSS的配置

uploadUrl 和 downloadUrl配置为外网可访问的地址,非实际的MINIO地址。即访问MINIO通过一个可外部访问的地址进行转换

cdn:
  oss:
    name: MINIO
    type: MINIO
    bucket: pamirs
    # uploadUrl 和 downloadUrl配置为外网可访问的地址,非实际的MINIO地址
    uploadUrl: http://127.0.0.1:8083
    downloadUrl: http://127.0.0.1:8083
    accessKeyId: svs7nKWRSrfFPH1X5FOy
    accessKeySecret: yoIq1T1w4ythggvL51rNyyiKST0KLPagUwB3C23A
    mainDir: assert/demo
    validTime: 3600000A
    timeout: 600000
    active: true
    referer:
    localFolderUrl:

NGINX配置(MINIO配置)

    upstream minio {
       #真实的MINIO的地址
        server 39.103.145.77:9000 weight=100 max_fails=2 fail_timeout=30s;
    }

    server {
        listen 8083;
        server_name 127.0.0.1;

        location / {
            proxy_set_header Host $http_host;
            proxy_set_header X-Forwarded-For $remote_addr;
            client_body_buffer_size 10M;
            client_max_body_size 50M;
            proxy_buffers 1024 4k;
            proxy_read_timeout 3000;
            proxy_next_upstream error timeout http_404;

            proxy_pass http://minio;
        }
    }

解决办法2

自定义实现MinioOssClient,跳过minio官方限制

package pro.shushi.pamirs.hr.core.file;

import io.minio.DateFormat;
import io.minio.MinioClient;
import io.minio.PostPolicy;
import io.minio.Result;
import io.minio.http.Method;
import io.minio.messages.Item;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.http.MediaTypeFactory;
import org.springframework.stereotype.Component;
import pro.shushi.pamirs.framework.connectors.cdn.client.AbstractFileClient;
import pro.shushi.pamirs.framework.connectors.cdn.configure.CdnConfig;
import pro.shushi.pamirs.framework.connectors.cdn.enmu.CDNExpEnum;
import pro.shushi.pamirs.framework.connectors.cdn.factory.CdnConfigRouter;
import pro.shushi.pamirs.framework.connectors.cdn.pojo.*;
import pro.shushi.pamirs.framework.connectors.cdn.utils.MinioMultipart;
import pro.shushi.pamirs.framework.connectors.cdn.utils.URLHelper;
import pro.shushi.pamirs.meta.annotation.fun.extern.Slf4j;
import pro.shushi.pamirs.meta.api.prefix.KeyPrefixManager;
import pro.shushi.pamirs.meta.common.exception.PamirsException;
import pro.shushi.pamirs.meta.common.spi.SPI;

import javax.annotation.PostConstruct;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import static pro.shushi.pamirs.framework.connectors.cdn.utils.MinioMultipart.generateRequestHeader;
import static pro.shushi.pamirs.meta.common.constants.CharacterConstants.SEPARATOR_SLASH;

@Slf4j
@Component
@SPI.Service(WzMiniOssClient.TYPE)
public class WzMiniOssClient extends AbstractFileClient {

    @Value("${cdn.oss.minioRealUrl}")
    private String minioRealUrl;

    public static final String TYPE = "WZ_MINIO";
    private static final String CONTENT_ENCODING = "Content-Encoding";
    private static final String CHUNKED = "aws-chunked";
    private static final String CONTENT_LENGTH = "x-amz-decoded-content-length";
    private static MinioClient DEFAULT_MINIO_CLIENT;

    private static final Map<String, MinioClient> MINIO_CLIENTS = new ConcurrentHashMap<>(2);

    private static final long MAX_OBJECT_SIZE = 5L * 1024 * 1024 * 1024 * 1024;
    // maxPartSize - maximum part size 5GiB for a single multipart upload operation
    private static final int MAX_MULTIPART_COUNT = 10000;
    // minimum allowed multipart size is 5MiB
    private static final int MIN_MULTIPART_SIZE = 5 * 1024 * 1024;

    // maximum allowed bucket policy size is 12KiB
    @Override
    protected String generatorFrontUploadFileKey(String filename) {
        CdnConfig cdnConfig = getCdnConfig();
        return getMinoFileKey(cdnConfig.getMainDir(), filename);
    }

    @Override
    protected String generatorFrontUploadUrl() {
        String uploadUrl = super.generatorFrontUploadUrl();
        if (!uploadUrl.endsWith(SEPARATOR_SLASH)) {
            uploadUrl = uploadUrl + SEPARATOR_SLASH;
        }
        return uploadUrl;
    }

    @Override
    protected Map<String, String> generatorFormData(String fileKey, CdnUploadFileRequest request) {
        CdnConfig cdnConfig = getCdnConfig();
        MinioClient minioClient = getMinioClient();
        String bucket = cdnConfig.getBucket();
        Long validTime = cdnConfig.getValidTime();
        try {
            PostPolicy postPolicy = new PostPolicy(bucket, fileKey, DateTime.now().plus(validTime));
            Map<String, String> formData = minioClient.presignedPostPolicy(postPolicy);
            formData.remove("bucket");
            formData.put("key", fileKey);
            return formData;
        } catch (Exception e) {
            throw PamirsException.construct(CDNExpEnum.MINIO_GET_FORM_DATA_ERROR, e).errThrow();
        }
    }

    @Override
    protected String getDefaultBaseDownloadUrl(CdnConfig cdnConfig) {
        return cdnConfig.getDownloadUrl() + SEPARATOR_SLASH + cdnConfig.getBucket();
    }

    @Override
    protected String getDefaultBaseUploadUrl(CdnConfig cdnConfig) {
        return minioRealUrl + SEPARATOR_SLASH + cdnConfig.getBucket();
    }

    @Override
    protected CdnMultipartUploadData generatorMultipartFileFormData(String fileKey, CdnUploadFileRequest request) {
        List<CdnChunkFile> chunkFiles = getChunkFiles(request);
        if (chunkFiles == null) {
            return null;
        }
        fileKey = URLHelper.encodeFileName(fileKey);
        CdnConfig cdnConfig = getCdnConfig();
        String accessKeyId = cdnConfig.getAccessKeyId();
        String accessKeySecret = cdnConfig.getAccessKeySecret();

        String date = new DateTime().toString(DateFormat.AMZ_DATE_FORMAT);
        String baseUploadUrl = getBaseUploadUrl();
        String uploadUrl = baseUploadUrl + SEPARATOR_SLASH + fileKey;

        //获取uploadId
        String uploadId = MinioMultipart.initMultipartUpload(baseUploadUrl, fileKey, accessKeyId, accessKeySecret, date);
        if (uploadId == null) {
            return null;
        }

        //构建分片上传参数
        CdnMultipartUploadData multipartUploadData = new CdnMultipartUploadData();
        List<CdnSingleUploadData> uploadDataList = new ArrayList<>();
        for (CdnChunkFile chunkFile : chunkFiles) {
            String partNumber = chunkFile.getPartNumber().toString();
            String fileSize = chunkFile.getFileSize().toString();
            CdnSingleUploadData singleUploadData = new CdnSingleUploadData();
            singleUploadData.setHttpMethod(Method.PUT.name());
            String url = String.format("%s?uploadId=%s&partNumber=%s", uploadUrl, uploadId, partNumber);
            singleUploadData.setUploadUrl(url);

            Map<String, String> uploadHeaders = new HashMap<>();
            uploadHeaders.put(CONTENT_ENCODING, CHUNKED);
            uploadHeaders.put(CONTENT_LENGTH, fileSize);
            try {
                generateRequestHeader(uploadHeaders, url, Method.PUT.toString(), accessKeyId, accessKeySecret, date);
            } catch (MalformedURLException | NoSuchAlgorithmException | InvalidKeyException e) {
                log.error("Minio 获取分片 生成请求头失败", e);
                return null;
            }
            singleUploadData.setUploadHeaders(uploadHeaders);
            uploadDataList.add(singleUploadData);
        }
        multipartUploadData.setUploadDataList(uploadDataList);

        //合并所有分片上传参数
        CdnCompleteUploadData completeUploadData = new CdnCompleteUploadData();
        String uploadCompleteUrl = baseUploadUrl + SEPARATOR_SLASH + fileKey + "?uploadId=" + uploadId;
        completeUploadData.setUploadUrl(uploadCompleteUrl);

        Map<String, String> uploadCompleteHeaders = new HashMap<>();
        try {
            generateRequestHeader(uploadCompleteHeaders, uploadCompleteUrl, Method.POST.toString(), accessKeyId, accessKeySecret, date);
        } catch (MalformedURLException | NoSuchAlgorithmException | InvalidKeyException e) {
            log.error("Minio 合并分片 生成请求头失败", e);
            return null;
        }
        completeUploadData.setUploadHeaders(uploadCompleteHeaders);
        completeUploadData.setUploadData("`<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + "\n<CompleteMultipartUpload>\n" + "${parts}" + "</CompleteMultipartUpload>`");
        completeUploadData.setUploadPartData("`<Part>\n" + "<PartNumber>${partNumber}</PartNumber>\n" + "<ETag>${response.partKey}</ETag>\n" + "</Part>\n`");

        Map<String, String> uploadCompletePartContext = new HashMap<>();
        uploadCompletePartContext.put("partNumber", "partNumber");
        uploadCompletePartContext.put("response.partKey", "headers.etag");
        completeUploadData.setUploadPartContext(uploadCompletePartContext);
        multipartUploadData.setCompleteUploadData(completeUploadData);
        return multipartUploadData;
    }

    @Override
    public CdnFile upload(String fileName, byte[] data) {
        return upload(fileName, new ByteArrayInputStream(data));
    }

    @Override
    public CdnFile upload(String fileName, InputStream inputStream) {
        CdnConfig cdnConfig = getCdnConfig();
        MinioClient minioClient = getMinioClient();
        CdnFile cdnFile = new CdnFile();
        String fileKey = getMinoFileKey(cdnConfig.getMainDir(), fileName);
        String bucket = cdnConfig.getBucket();
        try {
            Map<String, String> headerMap = new HashMap<>();
            String contentType = MediaTypeFactory.getMediaType(fileName).orElse(MediaType.APPLICATION_OCTET_STREAM).toString();
            headerMap.put("Content-Type", contentType);
            minioClient.putObject(bucket, fileKey, inputStream, Long.valueOf(inputStream.available()), headerMap, null, contentType);
            String url = getBaseDownloadUrl() + SEPARATOR_SLASH + fileKey;
            cdnFile.setType(FILE_TYPE);
            cdnFile.setName(fileName);
            cdnFile.setUrl(url);
        } catch (Exception e) {
            log.error("MINIO文件上传服务出错!", e);
            try {
                inputStream.close();
            } catch (IOException ex) {
                throw new RuntimeException(ex);
            }
        }
        return cdnFile;
    }

    @Override
    public String uploadByFileName(String fileName, byte[] data) {
        return uploadByFileName(fileName, new ByteArrayInputStream(data));
    }

    @Override
    public String uploadByFileName(String fileName, InputStream inputStream) {
        CdnConfig cdnConfig = getCdnConfig();
        MinioClient minioClient = getMinioClient();

        String keyPrefix = KeyPrefixManager.generate(SEPARATOR_SLASH, SEPARATOR_SLASH);
        String fileKey = cdnConfig.getMainDir() + keyPrefix + fileName;
        String bucket = cdnConfig.getBucket();
        Map<String, String> headerMap = new HashMap<>();
        String contentType = MediaTypeFactory.getMediaType(fileName).orElse(MediaType.APPLICATION_OCTET_STREAM).toString();
        headerMap.put("Content-Type", contentType);
        try {
            minioClient.putObject(bucket, fileKey, inputStream, contentType);
            return getBaseDownloadUrl() + SEPARATOR_SLASH + fileKey;
        } catch (Exception e) {
            log.error("MINIO文件上传服务出错!", e);
        }
        return null;
    }

    @Override
    public String getDownloadUrl(String fileName) {
        CdnConfig cdnConfig = getCdnConfig();
        String keyPrefix = KeyPrefixManager.generate(SEPARATOR_SLASH, SEPARATOR_SLASH);
        String fileKey = cdnConfig.getMainDir() + keyPrefix + fileName;
        return generatorDownloadUrl(fileKey);
    }

    @Override
    public void deleteByFolder(String folder) {
        CdnConfig cdnConfig = getCdnConfig();
        MinioClient minioClient = getMinioClient();
        String folderKey = getMinoFolder(cdnConfig.getMainDir(), folder);
        String bucket = cdnConfig.getBucket();

        try {
            // 获取指定前缀下的所有对象
            Iterable<Result<Item>> objects = minioClient.listObjects(bucket, folderKey, true);

            // 遍历对象列表,获取对象名
            List<String> objectNames = new ArrayList<>();
            for (Result<Item> result : objects) {
                Item item = result.get();
                objectNames.add(item.objectName());
            }
            minioClient.removeObjects(bucket, objectNames);
            log.info("deleteByFolder folder:[{}] 成功", folder);
        } catch (Exception e) {
            log.error("deleteByFolder folder:[{" + folder + "}] 失败", e);
        }
    }

    @Override
    public InputStream getDownloadStream(String fileKey) {
        CdnConfig cdnConfig = getCdnConfig();
        MinioClient minioClient = getMinioClient();
        String bucket = cdnConfig.getBucket();
        String bucketPrefix = bucket + SEPARATOR_SLASH;
        if (fileKey.startsWith(bucketPrefix)) {
            fileKey = fileKey.substring(bucketPrefix.length());
        }
        try {
            return minioClient.getObject(bucket, fileKey);
        } catch (Throwable e) {
            log.error("MINIO读取文件IO异常", e);
        }
        return null;
    }

    @Override
    public String fetchContent(String fileName) {
        try {
            CdnConfig cdnConfig = getCdnConfig();
            MinioClient minioClient = getMinioClient();
            String bucket = cdnConfig.getBucket();
            String keyPrefix = KeyPrefixManager.generate(SEPARATOR_SLASH, SEPARATOR_SLASH);
            String fileKey = cdnConfig.getMainDir() + keyPrefix + fileName;
            InputStream inputStream = minioClient.getObject(bucket, fileKey);
            String content = IOUtils.toString(inputStream);
            inputStream.close();
            return content;
        } catch (Throwable e) {
            log.error("MINIO读取文件IO异常", e);
        }
        return null;
    }

    @Override
    public void deleteByFilename(String filename) {
        CdnConfig cdnConfig = getCdnConfig();
        MinioClient minioClient = getMinioClient();
        String keyPrefix = KeyPrefixManager.generate(SEPARATOR_SLASH, SEPARATOR_SLASH);
        String fileKey = cdnConfig.getMainDir() + keyPrefix + filename;
        String bucket = cdnConfig.getBucket();
        try {
            minioClient.removeObject(bucket, fileKey);
        } catch (Exception e) {
            log.error("MINIO文件上传服务出错!", e);
        }
    }

    @Override
    public boolean isExistByFilename(String filename) {
        CdnConfig cdnConfig = getCdnConfig();
        MinioClient minioClient = getMinioClient();
        String keyPrefix = KeyPrefixManager.generate(SEPARATOR_SLASH, SEPARATOR_SLASH);
        String fileKey = cdnConfig.getMainDir() + keyPrefix + filename;
        String bucket = cdnConfig.getBucket();
        try {
            minioClient.statObject(bucket, fileKey);
            return true;
        } catch (Exception e) {
            log.error("MINIO文件上传服务出错!", e);
        }
        return false;
    }

    /**
     * 静态文件只有路径/用于初始化文件放置的路径
     * <p>
     * 配置项 DownloadUrl 必须带 协议头:HTTP/HTTPS
     *
     * @return
     */
    @Override
    public String getStaticUrl() {
        CdnConfig cdnConfig = getCdnConfig();
        if (cdnConfig.getAppLogoUseCdn() != null && cdnConfig.getAppLogoUseCdn()) {
            return minioRealUrl + SEPARATOR_SLASH + cdnConfig.getBucket();
        } else {
            return CdnConfig.defaultCdnUrl;
        }
    }

    // 客户特定场景需要(目前有客户需要),不要改变开放级别。
    // 正确的获取文件系统Client的方式:FileClientFactory.getClient()
    public MinioClient getMinioClient() {
        String routerKey = CdnConfigRouter.get();
        if (StringUtils.isBlank(routerKey)) {
            return DEFAULT_MINIO_CLIENT;
        }
        return MINIO_CLIENTS.computeIfAbsent(routerKey, (key) -> generatorMinioClient(getCdnConfig()));
    }

    protected MinioClient generatorMinioClient(CdnConfig cdnConfig) {
        MinioClient minioClient = null;
        String accessKeyId = cdnConfig.getAccessKeyId();
        String accessKeySecret = cdnConfig.getAccessKeySecret();
        // String endpoint = cdnConfig.getUploadUrl();
        Long timeout = cdnConfig.getTimeout();
        try {
            minioClient = new MinioClient(minioRealUrl, accessKeyId, accessKeySecret);
            minioClient.setTimeout(timeout, timeout, timeout);
            String bucketName = cdnConfig.getBucket();
            boolean isExist = minioClient.bucketExists(bucketName);
            if (!isExist) {
                minioClient.makeBucket(bucketName);
            }
        } catch (Exception e) {
            log.error("Minio 连接异常,bucketName: " + cdnConfig.getBucket() + "", e);
        }
        return minioClient;
    }

    @PostConstruct
    public void init() {
        CdnConfig cdnConfig = getCdnConfig();
        if (WzMiniOssClient.TYPE.equals(cdnConfig.getType())) {
            DEFAULT_MINIO_CLIENT = generatorMinioClient(cdnConfig);
        }
    }

    //FileKey不能以 / 开头,否则老版本的MINIO会有问题
    protected String getMinoFileKey(String mainDir, String filename) {
        String keyPrefix = KeyPrefixManager.generate(SEPARATOR_SLASH, SEPARATOR_SLASH);
        if (StringUtils.isNotBlank(keyPrefix)) {
            if (!keyPrefix.endsWith(SEPARATOR_SLASH)) {
                keyPrefix = keyPrefix + SEPARATOR_SLASH;
            }
        }
        return getFileDir(mainDir) + SEPARATOR_SLASH + keyPrefix + filename;
    }

    protected String getMinoFolder(String mainDir, String folder) {
        String keyPrefix = KeyPrefixManager.generate(SEPARATOR_SLASH, SEPARATOR_SLASH);
        if (StringUtils.isNotBlank(keyPrefix)) {
            if (!keyPrefix.endsWith(SEPARATOR_SLASH)) {
                keyPrefix = keyPrefix + SEPARATOR_SLASH;
            }
        }
        return getFileDir(mainDir) + SEPARATOR_SLASH + keyPrefix + folder;
    }
}

nginx 配置,拦截前端访问地址,转到实际minio地址

location /minio/ {
    rewrite ^/minio(/.*)$ $1 break; # 去除/minio前缀
    proxy_pass http://39.103.145.77:9000;
}

oss配置
MINIO无公网访问地址下OSS的配置

Oinone社区 作者:望闲原创文章,如若转载,请注明出处:https://doc.oinone.top/yun-wei-shi-jian/7112.html

访问Oinone官网:https://www.oinone.top获取数式Oinone低代码应用平台体验

(0)
望闲的头像望闲数式管理员
上一篇 2024年5月8日 am2:46
下一篇 2024年5月11日 am10:52

相关推荐

  • Docker部署常见问题

    问题1:容器启动出现library initialization failed – unable to allocate file descriptor table – out of memory异常如何处理? 原因 不同操作系统安装Docker后,容器运行环境并不一致,需要对Docker运行参数进行调整。 解决方案 编辑/etc/systemd/system/docker.service文件, 有些系统该文件位置:/lib/systemd/system/docker.service 查看docker的systemd(docker.service)配置位置 systemctl status docker 查看docker的systemd配置位置 将下列参数进行修改 LimitNOFILE=65535 LimitNPROC=65535 LimitCORE=65535 执行以下脚本 systemctl daemon-reload systemctl restart docker 问题2:容器启动出现library initialization failed – unable to allocate file descriptor table – out of memorypanic: signal: aborted (core dumped)异常如何处理? 问题现象 1、 按照【问题1】的设置进行配置后,仍然不生效; 2、 尝试修改宿主机系统内核的ulimits,重启docker仍报错。修改docker.service(文件位置:/etc/systemd/system/docker.service文件, 有些系统该文件位置:/lib/systemd/system/docker.service) 解决方案 查看docker的systemd(docker.service)配置位置【问题1】中的办法 在ExecStart命令后加上创建容器的默认ulimit配置,如下,设置容器启动时的ulimit为65535:65535 –default-ulimit nofile=65535:65535 配置好后: ExecStart=/usr/bin/dockerd -H fd:// –containerd=/run/containerd/containerd.sock –default-ulimit nofile=65535:65535 执行以下脚本 systemctl daemon-reload systemctl restart docker 资料参考:https://blog.csdn.net/weixin_42241322/article/details/137122868 问题3:拉取设计器镜像报错 报错信息,拉取镜像harbor.oinone.top连不上。 docker login –username=schhsw_oinone harbor.oinone.top i Info → A Personal Access Token (PAT) can be used instead. To create a PAT, visit https://app.docker.com/settings Password: time="2025-02-27T11:24:58+08:00" level=info msg="Error logging in to endpoint, trying next endpoint" error="Get \"https://harbor.oinone.top/v2/\": dial tcp 0.0.0.0:443: connect: connection refused" Get "https://harbor.oinone.top/v2/": dial tcp 0.0.0.0:443: connect: connection refused kfpt@kfpt-virtual-machine:~$ sudo -i root@kfpt-virtual-machine:~# docker login –username=schhsw_oinone harbor.oinone.top i Info → A Personal Access Token (PAT) can be used instead. To create a PAT, visit https://app.docker.com/settings Password: Error response from daemon: Get "https://harbor.oinone.top/v2/": dial tcp 0.0.0.0:443: connect: connection refused 排查过程: 排除到后面发现原因是DNS配置的问题,换了一个阿里云的IP就可以了

    2025年3月13日
    47600
  • Oinone平台部署及依赖说明(v5.3)

    概述 名词解释 业务工程包:指平台提供的内置基础包和内置功能包。 设计器工程包:指模型设计器、界面设计器、流程设计器等相关依赖包。 父POM:仅声明依赖包版本的pom文件 启动工程POM:仅声明具体依赖包的pom文件,一般不用于指定版本。 业务工程部署 Oinone平台向合作伙伴提供前后端业务工程所需的全部依赖,依赖项的具体说明下面会分别介绍。 设计器部署 Oinone平台向合作伙伴提供了两种设计器部署方式: Docker镜像:支持amd64和arm64两种架构的操作系统。(推荐) JAR包:与Docker镜像中的内容完全一致。 使用JAR包直接启动需要使用Oinone专属启动器,Docker镜像已内置Oinone专属启动器。 PS:如遇到以上部署方式或提供的镜像无法满足部署需求时,请联系Oinone平台售后获取技术支持。 Docker镜像 体验镜像 包括所有设计器。 内置所有所需中间件,包括Mysql、Redis、Zookeeper、RocketMQ。 适用场景:用于快速体验Oinone平台全部功能 docker pull harbor.oinone.top/oinone/oinone-designer-full-v5.3:TAG 前后端一体部署镜像 包括所有设计器。 无内置中间件。 适用场景:用于便捷部署Oinone平台的前后端服务 docker pull harbor.oinone.top/oinone/oinone-designer-mini-v5.3:TAG 前后端一体部署镜像 – 流程设计器 仅包括流程设计器。 无内置中间件。 适用场景:用于便捷部署仅需流程设计器的Oinone平台的前后端服务。 docker pull harbor.oinone.top/oinone/workflow-designer-v5.3:TAG 后端部署镜像 用于前后端分别部署,包括所有设计器。 仅包含后端服务 无内置中间件 适用场景:Kubenetes部署;支持健康检查,前后端分离部署; docker pull harbor.oinone.top/oinone/designer-backend-v5.3:TAG 前端部署镜像 用于前后端分别部署,包括所有设计器。 仅包含前端服务 无内置中间件 适用场景:Kubenetes部署;支持健康检查,前后端分离部署; docker pull harbor.oinone.top/oinone/designer-frontend-v5.3:TAG 镜像拉取 以体验镜像为例,Oinone平台提供多种拉取镜像的方式。 # 获取混合架构镜像,支持amd64和arm64架构的操作系统 docker pull harbor.oinone.top/oinone/oinone-designer-full-v5.3:5.3.5 # 仅获取amd64架构镜像 docker pull harbor.oinone.top/oinone/oinone-designer-full-v5.3:5.3.5-amd64 # 仅获取arm64架构镜像 docker pull harbor.oinone.top/oinone/oinone-designer-full-v5.3:5.3.5-arm64 # 获取最新版镜像(每次拉取自动更新) docker pull harbor.oinone.top/oinone/oinone-designer-full-v5.3 PS:如镜像拉取过慢,可在确定操作系统架构的情况下获取amd64或arm64架构镜像。 JAR包获取 $VERSION:对应镜像版本号 包含所有设计器的后端JAR包下载路径示例 https://oinone-jar.oss-cn-zhangjiakou.aliyuncs.com/install/oinone-designer/pamirs-designer-boot-v5.3-$VERSION.jar 仅包含流程设计器的后端JAR包下载路径示例 https://oinone-jar.oss-cn-zhangjiakou.aliyuncs.com/install/workflow-designer/pamirs-workflow-designer-boot-v5.3-$VERSION.jar 后端依赖 Oinone平台后端使用Maven管理工具对所有依赖包进行版本管理。 版本说明 下面是在每个版本升级说明中提供的所有依赖版本,最新版本请查看对应的最新升级说明文档。 版本更新日志 <!– 平台基础 –> <oinone.version>5.3.5</oinone.version> <!– 设计器 –> <pamirs.workflow.designer.version>5.3.0</pamirs.workflow.designer.version> <pamirs.model.designer.version>5.3.2</pamirs.model.designer.version> <pamirs.ui.designer.version>5.3.3</pamirs.ui.designer.version> <pamirs.data.designer.version>5.3.0</pamirs.data.designer.version> <pamirs.dataflow.designer.version>5.3.0</pamirs.dataflow.designer.version> <pamirs.eip.designer.version>5.3.0</pamirs.eip.designer.version> <pamirs.microflow.designer.version>5.3.0</pamirs.microflow.designer.version> 依赖版本管理 为了方便合作伙伴更方便的对平台版本进行管理,平台提供了一体化的依赖管理依赖包oinone-bom。 <dependencyManagement> <dependencies> <dependency> <groupId>pro.shushi</groupId> <artifactId>oinone-bom</artifactId> <version>${oinone.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> 完整功能依赖示例 以下列举了除了设计器相关依赖外的全部依赖管理项,其通常使用在启动工程POM中。 <dependency> <groupId>pro.shushi.pamirs</groupId> <artifactId>a</artifactId> </dependency> <dependency> <groupId>pro.shushi.pamirs.boot</groupId> <artifactId>pamirs-boot-standard</artifactId> </dependency> <dependency> <groupId>pro.shushi.pamirs.metadata.manager</groupId> <artifactId>pamirs-metadata-manager</artifactId> </dependency> <dependency> <groupId>pro.shushi.pamirs.framework</groupId> <artifactId>pamirs-connectors-event-rocketmq</artifactId> </dependency> <!–<dependency>–> <!– <groupId>pro.shushi.pamirs.framework</groupId>–> <!– <artifactId>pamirs-connectors-event-kafka</artifactId>–> <!–</dependency>–> <!–<dependency>–> <!– <groupId>pro.shushi.pamirs.framework</groupId>–> <!– <artifactId>pamirs-connectors-event-rabbitmq</artifactId>–> <!–</dependency>–> <dependency> <groupId>pro.shushi.pamirs.boot</groupId> <artifactId>pamirs-distribution-id</artifactId> </dependency> <!–<dependency>–> <!– <groupId>pro.shushi.pamirs.distribution</groupId>–> <!– <artifactId>pamirs-distribution-faas</artifactId>–> <!–</dependency>–> <!–<dependency>–> <!– <groupId>pro.shushi.pamirs.distribution</groupId>–> <!– <artifactId>pamirs-distribution-gateway</artifactId>–> <!–</dependency>–> <!–<dependency>–> <!– <groupId>pro.shushi.pamirs.distribution</groupId>–> <!– <artifactId>pamirs-distribution-session</artifactId>–> <!–</dependency>–> <!–<dependency>–> <!– <groupId>pro.shushi.pamirs.distribution</groupId>–> <!– <artifactId>pamirs-distribution-session-cd</artifactId>–> <!–</dependency>–> <dependency> <groupId>pro.shushi.pamirs.core</groupId> <artifactId>pamirs-sequence</artifactId> </dependency> <dependency> <groupId>pro.shushi.pamirs.core</groupId> <artifactId>pamirs-expression-core</artifactId>…

    2025年1月8日
    99400
  • Oinone平台部署及依赖说明(v5.0)

    概述 名词解释 业务工程包:指平台提供的内置基础包和内置功能包。 设计器工程包:指模型设计器、界面设计器、流程设计器等相关依赖包。 父POM:仅声明依赖包版本的pom文件 启动工程POM:仅声明具体依赖包的pom文件,一般不用于指定版本。 业务工程部署 Oinone平台向合作伙伴提供前后端业务工程所需的全部依赖,依赖项的具体说明下面会分别介绍。 设计器部署 Oinone平台向合作伙伴提供了两种设计器部署方式: Docker镜像:支持amd64和arm64两种架构的操作系统。(推荐) JAR包:与Docker镜像中的内容完全一致。 使用JAR包直接启动需要使用Oinone专属启动器,Docker镜像已内置Oinone专属启动器。 PS:如遇到以上部署方式或提供的镜像无法满足部署需求时,请联系Oinone平台售后获取技术支持。 Docker镜像 体验镜像 包括所有设计器。 内置所有所需中间件,包括Mysql、Redis、Zookeeper、RocketMQ。 适用场景:用于快速体验Oinone平台全部功能 docker pull harbor.oinone.top/oinone/oinone-designer-full-v5.0:TAG 前后端一体部署镜像 包括所有设计器。 无内置中间件。 适用场景:用于便捷部署Oinone平台的前后端服务 docker pull harbor.oinone.top/oinone/oinone-designer-mini-v5.0:TAG 前后端一体部署镜像 – 流程设计器 仅包括流程设计器。 无内置中间件。 适用场景:用于便捷部署仅需流程设计器的Oinone平台的前后端服务。 docker pull harbor.oinone.top/oinone/workflow-designer-v5.0:TAG 后端部署镜像 用于前后端分别部署,包括所有设计器。 仅包含后端服务 无内置中间件 适用场景:Kubenetes部署;支持健康检查,前后端分离部署; docker pull harbor.oinone.top/oinone/designer-backend-v5.0:TAG 前端部署镜像 用于前后端分别部署,包括所有设计器。 仅包含前端服务 无内置中间件 适用场景:Kubenetes部署;支持健康检查,前后端分离部署; docker pull harbor.oinone.top/oinone/designer-frontend-v5.0:TAG 镜像拉取 以体验镜像为例,Oinone平台提供多种拉取镜像的方式。 # 获取混合架构镜像,支持amd64和arm64架构的操作系统 docker pull harbor.oinone.top/oinone/oinone-designer-full-v5.0:5.0.3.3 # 仅获取amd64架构镜像 docker pull harbor.oinone.top/oinone/oinone-designer-full-v5.0:5.0.3.3-amd64 # 仅获取arm64架构镜像 docker pull harbor.oinone.top/oinone/oinone-designer-full-v5.0:5.0.3.3-arm64 # 获取最新版镜像(每次拉取自动更新) docker pull harbor.oinone.top/oinone/oinone-designer-full-v5.0 PS:如镜像拉取过慢,可在确定操作系统架构的情况下获取amd64或arm64架构镜像。 JAR包获取 $VERSION:对应镜像版本号 包含所有设计器的后端JAR包下载路径示例 https://oinone-jar.oss-cn-zhangjiakou.aliyuncs.com/install/oinone-designer/pamirs-designer-boot-v5.0-$VERSION.jar 仅包含流程设计器的后端JAR包下载路径示例 https://oinone-jar.oss-cn-zhangjiakou.aliyuncs.com/install/workflow-designer/pamirs-workflow-designer-boot-v5.0-$VERSION.jar Kubernetes后端部署健康检查 准备健康检查脚本 将以下脚本放置在镜像中。/opt/pamirs/bin/healthCheck.sh为以下示例中所用脚本路径。(平台提供的设计器镜像有内置该脚本,并与示例路径一致) #!/usr/bin/env bash function sendDingTalkMessage() { url=$DING_TALK_ROBOT_URL if [ -z $url ]; then return -1 fi message=$DING_TALK_ROBOT_MESSAGE if [ -z $message ]; then return -1 fi message="$message\\nby kubernetes" isAtAll=$DING_TALK_ROBOT_IS_AT_ALL if [ $isAtAll ]; then isAtAll="true" message="$message\\n" else isAtAll="false" fi message="{\"msgtype\": \"text\",\"text\": {\"content\":\"$message\"}, \"isAtAll\": \"${isAtAll}\"}" curl "${url}" -H 'Content-Type: application/json' -d "${message}" } port=$1 callback=$2 OK=$(curl -s http://127.0.0.1:${port}/ruok) if [ "$OK" == "imok" ]; then if [ -n $callback ]; then $callback fi exit 0 else exit 1 fi 配置Kubernetes健康检查 kind: Deployment apiVersion: apps/v1 spec: template: spec: containers: – name: designer-backend env: – name: DING_TALK_ROBOT_URL #…

    2024年6月21日
    1.6K00
  • Oinone平台部署及依赖说明(v4.7)

    概述 名词解释 业务工程包:指平台提供的内置基础包和内置功能包。 设计器工程包:指模型设计器、界面设计器、流程设计器等相关依赖包。 父POM:仅声明依赖包版本的pom文件 启动工程POM:仅声明具体依赖包的pom文件,一般不用于指定版本。 业务工程部署 Oinone平台向合作伙伴提供前后端业务工程所需的全部依赖,依赖项的具体说明下面会分别介绍。 设计器部署 Oinone平台向合作伙伴提供了两种设计器部署方式: Docker镜像:支持amd64和arm64两种架构的操作系统。(推荐) JAR包:与Docker镜像中的内容完全一致。 使用JAR包直接启动需要使用Oinone专属启动器,Docker镜像已内置Oinone专属启动器。 PS:如遇到以上部署方式或提供的镜像无法满足部署需求时,请联系Oinone平台售后获取技术支持。 Docker镜像 体验镜像 包括所有设计器。 内置所有所需中间件,包括Mysql、Redis、Zookeeper、RocketMQ。 适用场景:用于快速体验Oinone平台全部功能 docker pull harbor.oinone.top/oinone/oinone-designer-full-v4.8:TAG 前后端一体部署镜像 包括所有设计器。 无内置中间件。 适用场景:用于便捷部署Oinone平台的前后端服务 docker pull harbor.oinone.top/oinone/oinone-designer-mini-v4.8:TAG 前后端一体部署镜像 – 流程设计器 仅包括流程设计器。 无内置中间件。 适用场景:用于便捷部署仅需流程设计器的Oinone平台的前后端服务。 docker pull harbor.oinone.top/oinone/workflow-designer-v4.8:TAG 镜像拉取 以体验镜像为例,Oinone平台提供多种拉取镜像的方式。 # 获取混合架构镜像,支持amd64和arm64架构的操作系统 docker pull harbor.oinone.top/oinone/oinone-designer-full-v4.8:4.8.19 # 仅获取amd64架构镜像 docker pull harbor.oinone.top/oinone/oinone-designer-full-v4.8:4.8.19-amd64 # 仅获取arm64架构镜像 docker pull harbor.oinone.top/oinone/oinone-designer-full-v4.8:4.8.19-arm64 # 获取最新版镜像(每次拉取自动更新) docker pull harbor.oinone.top/oinone/oinone-designer-full-v4.8 PS:如镜像拉取过慢,可在确定操作系统架构的情况下获取amd64或arm64架构镜像。 JAR包获取 $VERSION:对应镜像版本号 包含所有设计器的后端JAR包下载路径示例 https://oinone-jar.oss-cn-zhangjiakou.aliyuncs.com/install/oinone-designer/pamirs-designer-boot-v4.8-$VERSION.jar 仅包含流程设计器的后端JAR包下载路径示例 https://oinone-jar.oss-cn-zhangjiakou.aliyuncs.com/install/workflow-designer/pamirs-workflow-designer-boot-v4.8-$VERSION.jar 后端依赖 Oinone平台后端使用Maven管理工具对所有依赖包进行版本管理。 版本说明 下面是在每个版本升级说明中提供的所有依赖版本,最新版本请查看对应的最新升级说明文档。 版本更新日志 <!– 平台基础 –> <pamirs.middleware.version>4.7.8.5</pamirs.middleware.version> <pamirs.k2.version>4.7.8.7</pamirs.k2.version> <pamirs.framework.version>4.7.8.19</pamirs.framework.version> <pamirs.boot.version>4.7.8.12</pamirs.boot.version> <pamirs.distribution.version>4.7.8.5</pamirs.distribution.version> <!– 平台功能 –> <pamirs.metadata.manager>4.7.8</pamirs.metadata.manager> <pamirs.core.version>4.7.8.19</pamirs.core.version> <pamirs.workflow.version>4.7.8.16</pamirs.workflow.version> <pamirs.workbench.version>4.7.8</pamirs.workbench.version> <pamirs.data.visualization.version>4.7.8.1</pamirs.data.visualization.version> <!– 设计器 –> <pamirs.designer.common.version>4.7.8</pamirs.designer.common.version> <pamirs.flow.designer.base.version>4.7.8.1</pamirs.flow.designer.base.version> <pamirs.workflow.designer.version>4.7.8</pamirs.workflow.designer.version> <pamirs.model.designer.version>4.7.8</pamirs.model.designer.version> <pamirs.ui.designer.version>4.7.8.9</pamirs.ui.designer.version> <pamirs.data.designer.version>4.7.8</pamirs.data.designer.version> <pamirs.dataflow.designer.version>4.7.8.1</pamirs.dataflow.designer.version> <pamirs.eip.designer.version>4.7.8.1</pamirs.eip.designer.version> 完整依赖管理示例 以下列举了除了设计器相关依赖外的全部依赖管理项,其通常使用在父POM中。 <dependency> <groupId>pro.shushi.pamirs</groupId> <artifactId>pamirs-k2</artifactId> <version>${pamirs.k2.version}</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>pro.shushi.pamirs</groupId> <artifactId>pamirs-framework</artifactId> <version>${pamirs.framework.version}</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>pro.shushi.pamirs.boot</groupId> <artifactId>pamirs-boot-dependencies</artifactId> <version>${pamirs.boot.version}</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>pro.shushi.pamirs.core</groupId> <artifactId>pamirs-core-dependencies</artifactId> <version>${pamirs.core.version}</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>pro.shushi.pamirs</groupId> <artifactId>pamirs-distribution</artifactId> <version>${pamirs.distribution.version}</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>pro.shushi.pamirs.middleware</groupId> <artifactId>pamirs-middleware-dependencies</artifactId> <version>${pamirs.middleware.version}</version> <type>pom</type> <scope>import</scope> </dependency> <!–metadata manager–> <dependency> <groupId>pro.shushi.pamirs.metadata.manager</groupId> <artifactId>pamirs-metadata-manager</artifactId> <version>${pamirs.metadata.manager}</version> </dependency> <!–workflow–> <dependency> <groupId>pro.shushi.pamirs.workflow</groupId> <artifactId>pamirs-workflow</artifactId> <version>${pamirs.workflow.version}</version> <type>pom</type> <scope>import</scope> </dependency> <!–workbench–> <dependency> <groupId>pro.shushi.pamirs.work.bench</groupId> <artifactId>pamirs-work-bench</artifactId> <version>${pamirs.workbench.version}</version> <type>pom</type> <scope>import</scope> </dependency> <!–data-vi–> <dependency> <groupId>pro.shushi.pamirs.data.visualization</groupId> <artifactId>pamirs-data-visualization</artifactId> <version>${pamirs.data.visualization.version}</version> <type>pom</type> <scope>import</scope> </dependency> 完整功能依赖示例 以下列举了除了设计器相关依赖外的全部依赖管理项,其通常使用在启动工程POM中。 <dependency> <groupId>pro.shushi.pamirs</groupId> <artifactId>a</artifactId> </dependency>…

    2024年9月3日
    54000
  • 如何配置文件存储地址为相对路径

    介绍 大部分情况下,文件存储的配置都会配置uploadUrl和downloadUrl的值,这样图片和文件类型的业务字段会存储带域名的全路径文件地址,但是有些场景需要用户在专有内网的时候访问内网的文件存储地址,用户在公网的时候通过公网访问(一般情况下这个公网ip内部最后还是会转发到内网)文件存储地址,这个时候就要求存在数据库字段里的不能是全路径的地址,只能是想相对路径,这个时候可以通过前端访问的域名用nginx转发当前域名下的文件请求到所匹配环境的ip内的文件存储服务。 配置参考 cdn: oss: name: 本地文件NG系统 type: LOCAL bucket: pamirs # 此处可以不配置值,也可以配置相对路径前缀方便做转发 uploadUrl: downloadUrl: validTime: 3600000 timeout: 600000 active: true referer: localFolderUrl: /test/static 文件上传的路径同步配置nginx转发 location ~ /file/upload { // 此处配置为后端服务的ip+端口 proxy_pass http://127.0.0.1:8190; proxy_set_header X-Real-IP $remote_addr; proxy_set_header REMOTE-HOST $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } excel导入场景相对路径在后端代码内无法保存导入的文件 在导入任务的前置hook处理相对文件路径的前缀 package pro.shushi.pamirs.demo.core.hook; import org.apache.commons.lang.StringUtils; import org.springframework.stereotype.Component; import pro.shushi.pamirs.file.api.FileModule; import pro.shushi.pamirs.file.api.model.ExcelImportTask; import pro.shushi.pamirs.meta.annotation.Hook; import pro.shushi.pamirs.meta.api.core.faas.HookBefore; import pro.shushi.pamirs.meta.api.dto.fun.Function; @Component public class DemoExcelImportTaskActionHookBefore implements HookBefore { // 内网访问文件的地址 public static final String INNER_HOST = "http://127.0.0.1:6800"; @Override @Hook(module = {FileModule.MODULE_MODULE}, model = {ExcelImportTask.MODEL_MODEL}, fun = {"createImportTask"}) public Object run(Function function, Object… args) { if (args.length > 0) { ExcelImportTask excelImportTask = (ExcelImportTask) args[0]; if (excelImportTask != null && excelImportTask.getFile() != null && StringUtils.isNotBlank(excelImportTask.getFile().getUrl())) { excelImportTask.getFile().setUrl(INNER_HOST + excelImportTask.getFile().getUrl()); } } return null; } } 注意事项 上面的例子为本地文件NG系统的,如果是阿里云oss、7牛云等第三方文件存储服务,downloadUrl可以按上面方案直接配置,uploadUrl还是要按正常配置

    2024年6月21日
    1.0K00

Leave a Reply

登录后才能评论