IGServer服务发布自动化系列之——(四)地图文档发布为服务

前言

前面两章中,我们已经介绍了如何将地图文档和数据库文件上传至IGServer,并将数据库文件附加到IGServer的数据源中,这一章我们就来介绍如何使用IGServer提供的接口实现服务发布。

实现思路

调用该接口的效果和在MapGIS IGServer中选择服务发布-二维地图服务-地图服务基本一致,包括的参数也基本一致。通过该接口,可以将地图文档发布为MapGIS地图服务MapGIS要素服务WMS服务WFS服务WMTS服务OGC API-FeaturesArcGIS 要素服务ArcGIS 地图服务等。
示意图

  • 请求方式:POST
  • 接口路径:/manager/api/service/doc/publish
  • Query 参数
参数名类型是否必填默认值说明
namestring-服务名称
folderstring-文件夹名称
aliasNamestring""服务别名
pathstring-数据路径
idint64-服务ID,非空时表示更新服务
serviceTypestring-主服务类型,可选值:IGSRestMap, WMS, WFS, WMTS, ArcGISRestMapServer, ArcGISRestFeatureServer
dataSourceTypestring"MapGISMapx"数据来源类型
mapIndexint320发布地图索引
projSrsNamestring-动态投影参考系名称,格式如 EPSG:3857
map.enablebooleantrue是否开启地图服务
feature.enablebooleanfalse是否开启要素服务
wms.enablebooleanfalse是否开启 WMS 服务
wms.get-feature-infobooleanfalse是否开启 WMS GetFeatureInfo 接口
wfs.enablebooleanfalse是否开启 WFS 服务
wfs.transactionboolean-是否开启 WFS Transaction 接口
enable-dynamic-tileboolean-是否开启动态瓦片
tile-sizeint32512动态瓦片大小
tile-formatstring"png"动态瓦片格式,支持 png/gif/jpg
tile-start-levelint32-动态瓦片开始级数
tile-end-levelint32-动态瓦片结束级数
tile-originstring-动态瓦片原点,格式为 x,y
tile-rangestring-动态瓦片裁图范围,格式为 xmin,ymin,xmax,ymax
tile-resolution-or-scalestring-使用分辨率或比例尺,支持 scale/resolution
tile-resolutionsstring-分辨率数组,逗号分隔,长度 = 结束级数 - 开始级数 + 1
tile-scalesstring-比例尺数组,逗号分隔,长度同上
tileCacheTypestring-关联瓦片缓存类型,可选值:RelatedTileService, RelatedTileData
relatedTileServiceNamestring-关联瓦片服务名称(当 tileCacheType=RelatedTileService 时必填)
relatedTilePathstring-关联瓦片数据路径(当 tileCacheType=RelatedTileData 时必填)
wmts.enablebooleanfalse是否开启 WMTS 服务
wmts.get-feature-infobooleanfalse是否开启 WMTS GetFeatureInfo 接口
wmts.epsg-namestring""WMTS 关联的 EPSG 名称,格式如 EPSG:3857
arcgis.enable-map-servicebooleanfalse是否开启 ArcGIS 地图服务
arcgis.enable-feature-servicebooleanfalse是否开启 ArcGIS 要素服务
ogcFeatureEnabledbooleanfalse是否开启 OGC API-Features
enableHiRenderbooleanfalse是否开启快显
hiRenderVersionstring-快显版本,支持 1.0/2.0
mongoPathstring""快显缓存的 MongoDB 路径
overrideIfExistbooleanfalse如果服务存在,是否覆盖
servicePermissionInfostring-服务的访问权限信息(JSON 字符串)
  • body参数
字段名类型说明
serviceIdint64服务ID
serviceTypestring服务类型
userIdsint64[]用户ID列表
roleIdsint64[]角色ID列表
userGroupIdsint64[]用户组ID列表
isPublicboolean是否公开
{
  "servicePermissionInfo": [
    {
      "serviceId": 0,
      "serviceType": "string",
      "userIds": [
        0
      ],
      "roleIds": [
        0
      ],
      "userGroupIds": [
        0
      ],
      "isPublic": true
    }
  ]
}

实现代码

<script setup>
import { onMounted, reactive, ref } from 'vue'
import {
  Map, MapServer, IGSMapImageLayer, Extent
} from '@mapgis/webclient-common'
import { MapView } from '@mapgis/webclient-leaflet-plugin' // 使用leaflet
import '@mapgis/leaflet/dist/leaflet.css'// 导入leaflet样式文件
import L from '@mapgis/leaflet'

// 地图相关引用和变量
const mapContainerRef = ref(null)
let position = reactive({ x: 0, y: 0 })
let map, view, layer, innerView

// 分步骤功能相关引用和变量
const currentStep = ref(0) // 0: 文件上传, 1: 服务发布
const uploadRef = ref(null)
const publishRef = ref(null)

// 上传功能参数
const uploadParams = ref({
  unZip: false,
  uploadDir: '',
  saveUploadHistory: true,
  renameIfExist: true
})

// 服务发布参数
const publishParams = ref({
  path: '', // 数据路径
  name: '', // 服务名称
  aliasName: '', // 服务别名
  folder: '', // 文件夹名称
  serviceType: 'IGSRestMap', // 主服务类型,默认值为IGSRestMap
  dataSourceType: 'MapGISMapx', // 数据来源类型
  mapIndex: 0, // 发布地图索引
  projSrsName: '', // 动态投影参考系名称
  'map.enable': true, // 是否开启地图服务
  'feature.enable': false, // 是否开启要素服务
  'wms.enable': false, // 是否开启 WMS 服务
  'wms.get-feature-info': false, // 是否开启 WMS GetFeatureInfo 接口
  'wfs.enable': false, // 是否开启 WFS 服务
  'wfs.transaction': false, // 是否开启 WFS Transaction 接口
  description: '' // 服务描述
})

const uploadResult = ref(null)
const publishResult = ref(null)
const uploadedFiles = ref([])

// 文件列表数据
const fileList = ref([])
const loadingFiles = ref(false)

// 自定义上传请求
const customRequest = async (options) => {
  try {
    const formData = new FormData()
    // 添加文件参数
    formData.append('files', options.file)
    // 添加其他查询参数
    const queryParams = new URLSearchParams()
    queryParams.append('unZip', uploadParams.value.unZip)
    if (uploadParams.value.uploadDir) {
      queryParams.append('uploadDir', uploadParams.value.uploadDir)
    }
    queryParams.append('saveUploadHistory', uploadParams.value.saveUploadHistory)
    queryParams.append('renameIfExist', uploadParams.value.renameIfExist)
    // 构建完整URL
    const url = `${options.action}?${queryParams.toString()}`
    // 发送请求
    const response = await fetch(url, {
      method: 'POST',
      body: formData,
    })

    if (!response.ok) {
      throw new Error(`上传失败: ${response.status} ${response.statusText}`)
    }
    const data = await response.json()
    options.onSuccess(data)
    return data
  } catch (error) {
    options.onError(error)
    throw error
  }
}

// 发布服务请求
const publishService = async () => {
  try {
    if (!publishParams.value.name) {
      throw new Error('请输入服务名称')
    }
    if (!publishParams.value.path) {
      throw new Error('请选择数据路径')
    }

    // 构建请求参数(使用Query参数)
    const queryParams = new URLSearchParams()
    queryParams.append('path', publishParams.value.path)
    queryParams.append('name', publishParams.value.name)
    queryParams.append('aliasName', publishParams.value.aliasName)
    if (publishParams.value.folder) {
      queryParams.append('folder', publishParams.value.folder)
    }
    queryParams.append('serviceType', publishParams.value.serviceType)
    queryParams.append('dataSourceType', publishParams.value.dataSourceType)
    queryParams.append('mapIndex', publishParams.value.mapIndex)
    if (publishParams.value.projSrsName) {
      queryParams.append('projSrsName', publishParams.value.projSrsName)
    }
    queryParams.append('map.enable', publishParams.value['map.enable'])
    queryParams.append('feature.enable', publishParams.value['feature.enable'])
    queryParams.append('wms.enable', publishParams.value['wms.enable'])
    queryParams.append('wms.get-feature-info', publishParams.value['wms.get-feature-info'])
    queryParams.append('wfs.enable', publishParams.value['wfs.enable'])
    queryParams.append('wfs.transaction', publishParams.value['wfs.transaction'])
    if (publishParams.value.description) {
      queryParams.append('description', publishParams.value.description)
    }

    // 构建完整URL(使用用户提供的接口地址)
    const url = `http://localhost:8089/manager/api/service/doc/publish?${queryParams.toString()}`

    // 发送请求
    const response = await fetch(url, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded'
      }
    })

    if (!response.ok) {
      throw new Error(`服务发布失败: ${response.status} ${response.statusText}`)
    }
    const data = await response.json()
    publishResult.value = {
      success: true,
      message: `服务发布成功!服务名称: ${publishParams.value.name}`
    }
    console.log('服务发布成功:', data)
  } catch (error) {
    publishResult.value = {
      success: false,
      message: `服务发布失败: ${error.message || '未知错误'}`
    }
    console.error('服务发布失败:', error)
  }
}

// 提交上传
const submitUpload = () => {
  uploadRef.value.submit()
}

// 上传成功处理
const handleSuccess = (response) => {
  uploadResult.value = {
    success: true,
    message: `上传成功!共上传 ${response.uploadFiles.length} 个文件。`
  }
  uploadedFiles.value = response.uploadFiles || []
  console.log('上传成功:', response)

  // 如果有上传的文件路径,自动填充到服务发布的数据路径中
  if (uploadedFiles.value.length > 0) {
    publishParams.value.path = uploadedFiles.value[0].filePath || ''
    // 自动填充服务名称
    publishParams.value.name = extractFileName(uploadedFiles.value[0].fileName)
  }

  // 重新加载文件列表
  loadFileList()
}

// 从文件名中提取服务名称(去掉扩展名)
function extractFileName(fileName) {
  if (!fileName) return ''
  const lastDotIndex = fileName.lastIndexOf('.')
  if (lastDotIndex > 0) {
    return fileName.substring(0, lastDotIndex)
  }
  return fileName
}

// 加载文件列表
const loadFileList = async () => {
  loadingFiles.value = true
  try {
    // 从接口获取文件列表
    const response = await fetch('http://localhost:8089/igs/rest/services/system/ResourceServer/files?page=1&pageSize=100&f=json')
    const data = await response.json()

    // 存储文件列表,只显示mapx后缀的文件
    fileList.value = (data.files || []).filter(file =>
      file.fileName && file.fileName.toLowerCase().endsWith('.mapx')
    )

    // 如果没有选择数据路径但有文件,自动选择第一个
    if (!publishParams.value.path && fileList.value.length > 0) {
      handleFilePathChange(fileList.value[0].filePath)
    }
  } catch (error) {
    console.error('加载文件列表失败:', error)
  } finally {
    loadingFiles.value = false
  }
}

// 处理文件路径选择变化
const handleFilePathChange = (filePath) => {
  publishParams.value.path = filePath

  // 查找对应的文件,提取文件名填充服务名称
  if (filePath) {
    const selectedFile = fileList.value.find(file => file.filePath === filePath)
    if (selectedFile && selectedFile.fileName) {
      publishParams.value.name = extractFileName(selectedFile.fileName)
    }
  }
}

// 上传失败处理
const handleError = (error) => {
  uploadResult.value = {
    success: false,
    message: `上传失败: ${error.message || '未知错误'}`
  }
  console.error('上传失败:', error)
}

// 上传前校验
const beforeUpload = (file) => {
  // 可以在这里添加文件校验逻辑
  console.log('准备上传:', file.name)
  return true
}

// 文件大小格式化函数
function formatFileSize(bytes) {
  if (bytes === 0) return '0 Bytes';
  const k = 1024;
  const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
  const i = Math.floor(Math.log(bytes) / Math.log(k));
  return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
}

// 初始化地图和加载文件列表
onMounted( () => {
  // 加载文件列表
  loadFileList()
})


</script>

<template>
  <!-- 使用ref属性绑定DOM元素 -->
  <div id="map-container"></div>
  <div id="control">
    <el-card>
      <h2 style="margin-bottom: 20px; color: #303133;">数据处理与服务发布</h2>

      <!-- 步骤组件 -->
      <el-steps :active="currentStep" finish-status="success" style="margin-bottom: 20px;">
        <el-step title="文件上传"></el-step>
        <el-step title="服务发布"></el-step>
      </el-steps>
      <!-- 步骤内容区域 -->
      <!-- 第一步:文件上传 -->
      <div v-if="currentStep === 0">
        <p style="color: #606266; margin-bottom: 15px;">请上传数据文件,支持Shapefile、GeoJSON、影像等格式</p>

        <!-- Element Plus Upload 组件 -->
        <el-upload ref="uploadRef" action="http://localhost:8089/igs/rest/services/system/ResourceServer/files"
          :auto-upload="false" :multiple="true" :on-success="handleSuccess" :on-error="handleError"
          :before-upload="beforeUpload" :http-request="customRequest">
          <template #trigger>
            <el-button type="primary">选择文件</el-button>
          </template>

          <el-button class="ml-3" type="success" @click="submitUpload">
            开始上传
          </el-button>

          <template #tip>
            <div style="color: #606266; font-size: 14px; margin-top: 10px;">请上传文件,支持多选</div>
          </template>
        </el-upload>

        <!-- 上传参数设置 -->
        <div style="margin: 15px 0; padding: 15px; background-color: #f9f9f9; border-radius: 4px;">
          <div style="font-weight: bold; margin-bottom: 10px;">上传参数设置:</div>
          <el-checkbox v-model="uploadParams.unZip">ZIP文件自动解压</el-checkbox>
          <el-input v-model="uploadParams.uploadDir" placeholder="上传目录"
            style="width: 300px; margin-left: 15px;"></el-input>
        </div>

        <!-- 上传结果显示 -->
        <div v-if="uploadResult" :style="{ color: uploadResult.success ? '#67c23a' : '#f56c6c', marginTop: '10px' }">
          {{ uploadResult.message }}
        </div>

        <!-- 上传文件列表 -->
        <div v-if="uploadedFiles.length > 0" style="margin-top: 15px;">
          <div style="font-weight: bold; margin-bottom: 10px;">已上传文件:</div>
          <ul style="list-style: none; padding: 0;">
            <li v-for="(file, index) in uploadedFiles" :key="index" style="color: #606266;">
              {{ file.fileName }} ({{ formatFileSize(file.fileSize) }})
            </li>
          </ul>
        </div>

        <!-- 下一步按钮 -->
        <el-button v-if="uploadResult && uploadResult.success" type="primary" style="margin-top: 20px;"
          @click="currentStep = 1">
          下一步:服务发布
        </el-button>
      </div>

      <!-- 第二步:服务发布 -->
      <div v-if="currentStep === 1">
        <p style="color: #606266; margin-bottom: 15px;">请设置服务参数并发布</p>

        <!-- 服务发布参数设置 -->
        <div style="margin: 15px 0; padding: 15px; background-color: #f9f9f9; border-radius: 4px;">
          <div style="font-weight: bold; margin-bottom: 10px;">服务参数设置:</div>

          <el-form ref="publishRef" label-width="140px" style="max-width: 600px;">
            <!-- 必填参数 -->
            <el-form-item label="数据路径" prop="path" required>
              <el-select v-model="publishParams.path" placeholder="请选择数据路径" :loading="loadingFiles"
                style="z-index: 10000;">
                <el-option v-for="file in fileList" :key="file.id" :label="file.fileName"
                  :value="file.filePath"></el-option>
              </el-select>
              <el-button type="text" @click="loadFileList" style="margin-left: 10px;">刷新</el-button>
            </el-form-item>

            <el-form-item label="服务名称" prop="name" required>
              <el-input v-model="publishParams.name" placeholder="请输入服务名称"></el-input>
            </el-form-item>

            <!-- 可选参数 -->
            <el-form-item label="服务别名" prop="aliasName">
              <el-input v-model="publishParams.aliasName" placeholder="请输入服务别名"></el-input>
            </el-form-item>

            <el-form-item label="文件夹名称" prop="folder">
              <el-input v-model="publishParams.folder" placeholder="请输入文件夹名称"></el-input>
            </el-form-item>

            <el-form-item label="主服务类型" prop="serviceType">
              <el-select v-model="publishParams.serviceType" placeholder="请选择主服务类型">
                <el-option label="IGSRestMap" value="IGSRestMap"></el-option>
                <el-option label="WMS" value="WMS"></el-option>
                <el-option label="WFS" value="WFS"></el-option>
                <el-option label="WMTS" value="WMTS"></el-option>
                <el-option label="ArcGISRestMapServer" value="ArcGISRestMapServer"></el-option>
                <el-option label="ArcGISRestFeatureServer" value="ArcGISRestFeatureServer"></el-option>
              </el-select>
            </el-form-item>

            <el-form-item label="数据来源类型" prop="dataSourceType">
              <el-input v-model="publishParams.dataSourceType" placeholder="数据来源类型"></el-input>
            </el-form-item>

            <el-form-item label="发布地图索引" prop="mapIndex">
              <el-input-number v-model="publishParams.mapIndex" :min="0" style="width: 100%;"></el-input-number>
            </el-form-item>

            <el-form-item label="动态投影参考系" prop="projSrsName">
              <el-input v-model="publishParams.projSrsName" placeholder="格式如 EPSG:3857"></el-input>
            </el-form-item>

            <el-form-item label="服务描述" prop="description">
              <el-input type="textarea" v-model="publishParams.description" placeholder="请输入服务描述"></el-input>
            </el-form-item>

            <!-- 服务功能开关 -->
            <el-form-item label="服务功能设置">
              <div style="display: block;">
                <el-checkbox v-model="publishParams['map.enable']"
                  style="display: block; margin-bottom: 5px;">开启地图服务</el-checkbox>
                <el-checkbox v-model="publishParams['feature.enable']"
                  style="display: block; margin-bottom: 5px;">开启要素服务</el-checkbox>
                <el-checkbox v-model="publishParams['wms.enable']"
                  style="display: block; margin-bottom: 5px;">开启WMS服务</el-checkbox>
                <el-checkbox v-model="publishParams['wms.get-feature-info']"
                  style="display: block; margin-bottom: 5px;">开启WMS
                  GetFeatureInfo接口</el-checkbox>
                <el-checkbox v-model="publishParams['wfs.enable']"
                  style="display: block; margin-bottom: 5px;">开启WFS服务</el-checkbox>
                <el-checkbox v-model="publishParams['wfs.transaction']"
                  style="display: block; margin-bottom: 5px;">开启WFS
                  Transaction接口</el-checkbox>
              </div>
            </el-form-item>
          </el-form>
        </div>

        <!-- 服务发布操作按钮 -->
        <div style="margin-top: 20px;">
          <el-button type="default" @click="currentStep = 0">上一步</el-button>
          <el-button type="primary" style="margin-left: 10px;" @click="publishService">发布服务</el-button>
        </div>

        <!-- 发布结果显示 -->
        <div v-if="publishResult" :style="{ color: publishResult.success ? '#67c23a' : '#f56c6c', marginTop: '10px' }">
          {{ publishResult.message }}
        </div>
      </div>
    </el-card>
  </div>
</template>

<style scoped>
#map-container {
  width: 100%;
  height: 100%;
  cursor: pointer;
}

#control {
  position: absolute;
  bottom: 10px;
  right: 30px;
  z-index: 99999;
  user-select: none;
  max-width: 600px;
  padding: 0 10px;
}

.ml-3 {
  margin-left: 12px;
}

.el-steps {
  margin-bottom: 20px;
}

/* 为步骤内容区域添加过渡动画 */
.el-card>div {
  transition: all 0.3s ease;
}
</style>

发布界面

发布后的效果
服务详情预览

这样,就可以通过IGServer的接口,实现了各类服务发布。结合前几章介绍的数据上传、数据库附加、数据列表获取,就可以实现服务发布自动化,或将服务发布功能集成到您自己的系统中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值