1. 程式人生 > 其它 >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 文件

只要具備後端經驗的,檢視該文件毫無壓力。