项目中需要上传影像数据并能查看。于是想到的思路是:上传后发布为 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:DataUploadFiles"; //临时保存分块的目录 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 供调用。
点进每一个里面都是 Swagger 文档
只要具备后端经验的,查看该文档毫无压力。