前言
前面两章中,我们已经介绍了如何将地图文档和数据库文件上传至IGServer,并将数据库文件附加到IGServer的数据源中,这一章我们就来介绍如何使用IGServer提供的接口实现服务发布。
实现思路
调用该接口的效果和在MapGIS IGServer中选择服务发布-二维地图服务-地图服务基本一致,包括的参数也基本一致。通过该接口,可以将地图文档发布为MapGIS地图服务、MapGIS要素服务、WMS服务、WFS服务、WMTS服务、OGC API-Features、ArcGIS 要素服务、ArcGIS 地图服务等。

- 请求方式:POST
- 接口路径:/manager/api/service/doc/publish
- Query 参数
| 参数名 | 类型 | 是否必填 | 默认值 | 说明 |
|---|---|---|---|---|
name | string | 是 | - | 服务名称 |
folder | string | 否 | - | 文件夹名称 |
aliasName | string | 否 | "" | 服务别名 |
path | string | 是 | - | 数据路径 |
id | int64 | 否 | - | 服务ID,非空时表示更新服务 |
serviceType | string | 否 | - | 主服务类型,可选值:IGSRestMap, WMS, WFS, WMTS, ArcGISRestMapServer, ArcGISRestFeatureServer |
dataSourceType | string | 否 | "MapGISMapx" | 数据来源类型 |
mapIndex | int32 | 否 | 0 | 发布地图索引 |
projSrsName | string | 否 | - | 动态投影参考系名称,格式如 EPSG:3857 |
map.enable | boolean | 否 | true | 是否开启地图服务 |
feature.enable | boolean | 否 | false | 是否开启要素服务 |
wms.enable | boolean | 否 | false | 是否开启 WMS 服务 |
wms.get-feature-info | boolean | 否 | false | 是否开启 WMS GetFeatureInfo 接口 |
wfs.enable | boolean | 否 | false | 是否开启 WFS 服务 |
wfs.transaction | boolean | 否 | - | 是否开启 WFS Transaction 接口 |
enable-dynamic-tile | boolean | 否 | - | 是否开启动态瓦片 |
tile-size | int32 | 否 | 512 | 动态瓦片大小 |
tile-format | string | 否 | "png" | 动态瓦片格式,支持 png/gif/jpg |
tile-start-level | int32 | 否 | - | 动态瓦片开始级数 |
tile-end-level | int32 | 否 | - | 动态瓦片结束级数 |
tile-origin | string | 否 | - | 动态瓦片原点,格式为 x,y |
tile-range | string | 否 | - | 动态瓦片裁图范围,格式为 xmin,ymin,xmax,ymax |
tile-resolution-or-scale | string | 否 | - | 使用分辨率或比例尺,支持 scale/resolution |
tile-resolutions | string | 否 | - | 分辨率数组,逗号分隔,长度 = 结束级数 - 开始级数 + 1 |
tile-scales | string | 否 | - | 比例尺数组,逗号分隔,长度同上 |
tileCacheType | string | 否 | - | 关联瓦片缓存类型,可选值:RelatedTileService, RelatedTileData |
relatedTileServiceName | string | 否 | - | 关联瓦片服务名称(当 tileCacheType=RelatedTileService 时必填) |
relatedTilePath | string | 否 | - | 关联瓦片数据路径(当 tileCacheType=RelatedTileData 时必填) |
wmts.enable | boolean | 否 | false | 是否开启 WMTS 服务 |
wmts.get-feature-info | boolean | 否 | false | 是否开启 WMTS GetFeatureInfo 接口 |
wmts.epsg-name | string | 否 | "" | WMTS 关联的 EPSG 名称,格式如 EPSG:3857 |
arcgis.enable-map-service | boolean | 否 | false | 是否开启 ArcGIS 地图服务 |
arcgis.enable-feature-service | boolean | 否 | false | 是否开启 ArcGIS 要素服务 |
ogcFeatureEnabled | boolean | 否 | false | 是否开启 OGC API-Features |
enableHiRender | boolean | 否 | false | 是否开启快显 |
hiRenderVersion | string | 否 | - | 快显版本,支持 1.0/2.0 |
mongoPath | string | 否 | "" | 快显缓存的 MongoDB 路径 |
overrideIfExist | boolean | 否 | false | 如果服务存在,是否覆盖 |
servicePermissionInfo | string | 否 | - | 服务的访问权限信息(JSON 字符串) |
- body参数
| 字段名 | 类型 | 说明 |
|---|---|---|
serviceId | int64 | 服务ID |
serviceType | string | 服务类型 |
userIds | int64[] | 用户ID列表 |
roleIds | int64[] | 角色ID列表 |
userGroupIds | int64[] | 用户组ID列表 |
isPublic | boolean | 是否公开 |
{
"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的接口,实现了各类服务发布。结合前几章介绍的数据上传、数据库附加、数据列表获取,就可以实现服务发布自动化,或将服务发布功能集成到您自己的系统中。
377

被折叠的 条评论
为什么被折叠?



