其他分享
首页 > 其他分享> > GeoServer 一键发布 Raster 数据服务(分片上传、GDAL)

GeoServer 一键发布 Raster 数据服务(分片上传、GDAL)

作者:互联网

项目中需要上传影像数据并能查看。于是想到的思路是:上传后发布为 GeoServer 服务,再加载地图服务查看。

要实现该功能用到的技术还是不少的,下面就分别介绍下。

一、大文件上传

因为影像文件一般比较大,上传的时候就遇到了问题。

.net core 官网给出的是用流的方式上传,测试了没有成功。

最后选择的是分片上传实现的。

.net core + Vue 分片上传代码如下:

前端代码:

    customRequest() {
      const url = BASE_API + '/multipartupload'
      const createGuid = function() {
        function S4() {
          return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1)
        }
        return (S4() + S4() + '-' + S4() + '-' + S4() + '-' + S4() + '-' + S4() + S4() + S4())
      }
      // element 组件的 upload 绑定的文件,这里改变为正在上传状态,下面设置的上传百分比才有效
      this.uploadFile[0].status = 'uploading'
      this.fileUpload(url, this.uploadFile[0].raw, 0, createGuid())
    },
    // 上传文件,递归调用
    fileUpload(uploadUrl, file, chunk, guid) {
      // const file = option.file
      // 每次上传文件的大小 5M
      const chunkSize = 1024 * 1024 * 5
      // 最大上传大小  默认1000M
      const maxSize = 1024 * 1024 * 1000
      const maxChunk = Math.ceil((file.size / chunkSize))
      const formData = new FormData()
      // 将文件进行分段
      const fileSize = file.size
      if (fileSize > maxSize) {
        this.$message.error('文件大小不能超过1000M')
        return
      }

      // 当前上传进度
      const currentPercent = parseInt((chunk / maxChunk) * 100)
      // option.onProgress({ percent: currentPercent })
      formData.append('file', file.slice(chunk * chunkSize, (chunk + 1) * chunkSize))
      formData.append('name', file.name)
      formData.append('chunk', chunk)
      formData.append('maxChunk', maxChunk)
      formData.append('guid', guid)
      this.$request.post(uploadUrl, formData).then(result => {
        if (result.code === 0) {
          // upload 设置上传进度
          this.uploadFile[0].percentage = currentPercent
          if (result.message === '上传中') {
            this.fileUpload(uploadUrl, file, ++chunk, guid)
          } else {
            this.$message.success('文件上传成功')
            this.uploadFile[0].status = 'success'
          }
        } else {
          this.$message.error(result.message)
        }
      })
    }

后端代码:

        public async Task<ResponseData> MultipartUpload(MultipartUploadDTO multipartUpload)
        {
            string _targetFilePath = @"D:\Data\UploadFiles";
            //临时保存分块的目录
            var dir = Path.Combine(_targetFilePath, multipartUpload.Guid);
            if (!Directory.Exists(dir))
            {
                Directory.CreateDirectory(dir);
            }
            //分块文件名为索引名,更严谨一些可以加上是否存在的判断,防止多线程时并发冲突
            var filePath = Path.Combine(dir, multipartUpload.Chunk.ToString());
            //获取文件扩展名
            //var extension = file.FileName.Substring(file.FileName.LastIndexOf(".") + 1, (file.FileName.Length - file.FileName.LastIndexOf(".") - 1));
            var filePathWithFileName = string.Concat(filePath, multipartUpload.Name);
            using (var stream = new FileStream(filePathWithFileName, FileMode.Create))
            {
                await multipartUpload.Flie.CopyToAsync(stream);
            }

            //如果是最后一个分块, 则合并文件
            string message = "上传中";
            string path = "";
            if (multipartUpload.Chunk == multipartUpload.MaxChunk - 1)
            {
                path = await MergeFileAsync(multipartUpload.Name, multipartUpload.Guid, _targetFilePath);
                message = "上传完成!";
            }
            return new ResponseData() { Code = 0, Message = message, Data = path };
        }

        /// <summary>
        /// 合并分片的文件
        /// </summary>
        /// <param name="fileName"></param>
        /// <param name="guid"></param>
        /// <returns></returns>
        private async Task<string> MergeFileAsync(string fileName, string guid,string _targetFilePath)
        {
            //临时文件夹
            string dir = Path.Combine(_targetFilePath, guid);
            //最终的文件名
            string finalName = $"{DateTime.Now:yyyyMMddHHmmssff}{fileName}";
            string finalPath = Path.Combine(_targetFilePath, finalName);

            //获得下面的所有文件
            var files = Directory.GetFiles(dir);
            using (var fs = new FileStream(finalPath, FileMode.Create))
            {
                //排一下序,保证从0-N Write
                var fileParts = files.OrderBy(x => x.Length).ThenBy(x => x);
                foreach (var part in fileParts)
                {
                    var bytes = await File.ReadAllBytesAsync(part);
                    await fs.WriteAsync(bytes, 0, bytes.Length);
                    bytes = null;
                    //删除分块
                    File.Delete(part);
                }
                await fs.FlushAsync();
                fs.Close();
                //删除临时文件夹和分片文件
                Directory.Delete(dir);
            }

            return finalName;
        }

主要思路是:把同一个文件分成指定大小字节(byte)分片上传,全部上传后再合并为同一个文件。

二、.NET Core 调用 GDAL 

要发布 Raster 服务,需要知道文件的一些基本信息:坐标系、坐标范围等信息。

GDAL 可以很方便操作 GIS 数据,GDAL 官方给的类库是 C++ 版本。

要调用的话可以自己下载对应的 dll 进行调用。

在这里查找到了一个已经封装好的类库,在这里直接调用。

MaxRev.Gdal.Core

使用还需要注意几点:

1、需要的对应运行时:MaxRev.Gdal.LinuxRuntime.Minimal、MaxRev.Gdal.WindowsRuntime.Minimal (根据自己的平台来选择)

2、使用前初始化配置:GdalBase.ConfigureAll()

在项目中使用的主要功能有:

// 读取数据
Dataset dataset = Gdal.Open(tiffFile, Access.GA_ReadOnly);
double[] tr = new double[6];
// 获取 transform 数据
dataset.GetGeoTransform(tr);
// 获取 x、y 像素数
int xSize = dataset.RasterXSize;
int ySize = dataset.RasterYSize;
// 获取坐标信息
string projection = dataset.GetProjection();

三、GeoServer REST API 使用

上面的准备工作都已经完成,后面就是要发布对应的 Raster 服务了。

GeoServer 提供了全部功能的 REST API 供调用。

官方 REST 文档

点进每一个里面都是 Swagger 文档

只要具备后端经验的,查看该文档毫无压力。

标签:数据服务,Raster,const,string,S4,file,GeoServer,var,上传
来源: https://www.cnblogs.com/zhurong/p/15384912.html