将fme的模板作为web端的后台计算模型,django实现模板集成

     阅读:30

作者:努力的悟空


前言

从开始接触fme到发现可以用系统dos指令能调用fmw模板的时候,我就一直在思考,能否将模板集成与网站后台,然后用前端传递参数,最终用后台通过dos指令来调用模板,然后将结果反馈到前端呢。基于这个出发点,我研究了一个月,最终是将全套流程实现了。


一、需要用到的技术栈

1.HTML5+CSS+JAVASCRIPT

前端三件套,负责我们前端网页的样式,功能,以及用户交互,数据整合提交后台。

2.python+Django

通过Django来将前端,fme模板,数据库,路由进行联合,也就是django的MTV框架来实现我们的全套流程。Django作为纯python编写的框架,能够应用于各种web的后台环境。

3.postgresql+redis+celery

postgresql和mysql任选其一都可,目的为了存储路径信息和用户信息,redis数据库是为了实现异步请求功能,用于存储异步任务队列,celery技术则负责来实现异步任务功能

4.uWSGI+Nginx

Django部署上线最适合的服务器组合,uWSGI负责实现WSGI,uWSGI,HTTP等网络协议,再用Nginx的HttpUwsgiModule来实现和uWSGI的交互。通过这种方式,能最大程度实现多用户访问时候,系统的稳定性。

5.FME

fme是本次课题研究的核心,上述技术栈都是围绕如何将fme的模板融合到web体系并实现多用户无需安装配置任何环境,便可以实现多种复杂数据一键处理,并且享有高配置的运行速度。

比如我们手上有如下这么一个模板,但是用户使用需要安装fme,如何模板中涉及调用python的第三方库,我们 还需要给用户配置python环境才能使用。通过这个课题,我们需要实现在web前端界面来实现整个模板的运行。

二、技术突破点

1.如何将前端的表单信息传输到后台

django自带的框架能非常简单方便的将前端的表单信息按name获取,前端的表单信息因为安全性,一般考虑post提交,在前端url对应的视图函数中通过post.get方法便可以获得表单提交的参数。因为表单提交的参数为字典格式(name:value)。所以我们需要在html5中设置好自己的表单中的name

		<form enctype="multipart/form-data" action="" method="post">
            {% csrf_token %}
			输入你要扇出的字段名字
			<input tpye="text" name="sczd"/>
			<br>

			选择你的线条颜色
			<select name="color">
				<option value="1,0,0">红色</option>
				<option value="255,255,127" selected">黄色</option>
				<option value="85,170,255">蓝色</option>
				<option value="0,255,0">绿色</option>
			</select>

                <input type="file" multiple name="myfile" value=""/>
                <br>
                <input type="submit" value="提交并获得成果"/>
            </form>>
    zd=request.POST.get('sczd')
    color=request.POST.get('color')

2.如何将数据打包并封装为dos指令

dos指令调用fme的指令通常为"C:\Program Files\FME\fme.exe" D:\fmw\kml制作.fmw --导入你的shp文件 "$(FME_MF_DIR_USERTYPED)测试文件.shp" --输入你要扇出的字段名 "村" --线条颜色 "1,0,0" --FME_LAUNCH_VIEWER_APP "YES"这种格式,用--+参数名 再加具体参数的格式来完成参数的配置,前面则输入为fme.exe的路径以及fmw的路径,一定要记得用空格隔开。那么我们封装为dos指令便变得很简单,需要注意的是一定要使用大量的转义字符才能让路径的\ ""显示正常

        aa=str("\"""C:\\Program Files\\FME\\fme.exe""\" D:\\fmw\\kml制作.fmw --导入你的shp文件 \"""{}""\" --输入你要扇出的字段名 \"""{}""\" --线条颜色 \"""{}""\" --FME_LAUNCH_VIEWER_APP \"""YES""\"   ".format(road2,zd,color))

3.设置动态唯一路径

需要注意的是,我们实现的功能是通过前端传递的参数,来用fme模板计算得出结果数据,然后我们需要将结果数据存储为一个到一个可获得的动态的唯一路径。所以我们需要通过uuid和time来定义唯一bsm和时间戳来让每个用户的获取的成果是唯一路径。然后将该路径存储到数据库中。

首先我们需要将uuid和time 以及成果路径定义为全局变量。这样才能在其他函数中调用该变量。

    global file_path
    global uid
    global time1

4.实现dos调用模板

os方法自带os.system和os.popen来直接执行dos语句,但是用os会出现以下情况,就是因为调用os是独立开辟新进程,所以代码不会等os指令运行结束之后才进入下一行,而是直接进入下一行,这样我们就无法获取成果包,并进行压缩,后续也会报错。所以就引入了subporcess库。

通过该方式调用dos指令,代码会等待fmw运行完毕 在进行下一步。

ex=subprocess.Popen(aa,stdout=subprocess.PIPE, shell=True)

out, err = ex.communicate()

status = ex.wait()

5.实现将成果文件夹压缩为zip

我写了一个压缩函数放在django的应用层中,py文件名为zippp

 该函数设定了两个参数,一个输入路径 一个输入路径,可以将文件夹内部内容遍历并压缩为一个整包

import zipfile
import os


def zipDir(dirpath, outFullName):

    zip = zipfile.ZipFile(outFullName, "w", zipfile.ZIP_DEFLATED)
    for path, dirnames, filenames in os.walk(dirpath):
        # 去掉目标跟路径,只对目标文件夹下边的文件及文件夹进行压缩
        fpath = path.replace(dirpath, '')

        for filename in filenames:
            zip.write(os.path.join(path, filename), os.path.join(fpath, filename))
    zip.close()

我们只需要在主函数中调用该压缩函数,便可以将文件压缩到指定路径。

 zipDir(str('D:\\file' + '\\{}'.format(uid) + '\\{}'.format(time1)+'\\kml成果'),str('D:\\file' + '\\{}'.format(uid) + '\\{}\\kml成果.zip'.format(time1)))

6.实现用户文件的上传和最终成果的下载

用户在web端传入文件,参数等信息后我们需要在后端计算完毕并反馈给前端。

因为用户上传的文件大多数情况会有多文件类型,所以我们html5中要 multiple名称,但是django中,单个获取file只能获取多上传文件的最后一个,所以我们需要将传入的多文件进行遍历,并依次写出到我们的指定路径中。

#html 文件

<input type="file" multiple name="myfile" value=""/>


#view主函数


       for ff in myfile:

            road = os.path.join(aaA, ff.name)

            #print(road)

            f = open(road, 'wb+')
            for chunk in ff.chunks():
                f.write(chunk)
            f.close()

文件的上传则很简单,django中有现成的功能,但是注意我使用的StreamingHttpResponse,因为该方法的适用范围很广,适合大批量的文件传输,并不会占用过多的内存资源

        file_path = str('D:\\file' + '\\{}'.format(uid) + '\\{}\\kml成果.zip'.format(time1))
        try:
            r = StreamingHttpResponse(open(file_path, 'rb'))
            print(file_path)
            r['content_type'] = 'application/octet-stream'
            r['Content-Disposition'] = 'attachment;filename=kml.zip'

            return r

        except:
            pass

7.整个流程的实现

我们以一个小功能集为案例,首先写一个前端表单收集用户信息

输入文件和信息

 

 成功获得下载内容,该fme模板是一个简单的shp 转kml并按选择字段扇出的模板

三、存在问题和改进方案

虽然我们实现了全套流程。但是有一个巨大的缺点就是如果多用户同时访问同时提交任务,因为Django处理用户请求时,视图函数往往都是单线程执行,这样就会导致用户获取的成果会产生冲突。所以我们引入了异步请求,启动celery和redis数据库来完成异步任务。实现这个需要提前修改前端的html模板,增加一个模板的下载显示,并将提前将信息存入我们的数据库,再异步任务完成后,页面能自动根据提前存入数据库的信息,来获得下载内容。

后续可以通过fme log 模块来记录模板的运行进程。这样可以在前端增加一个进度条,还可以设置后台admin管理系统,让非技术人员也能在后台统一管理模板。


总结

经过最近一个多月的web学习,让我明白了bs开发的能力,通过bs开发,我们能将fme的模板建立为一个工作站,用户可以根据需求选取自己适合的功能来处理自己的数据。我要学习的路也还很长,web的设置的技术栈实在是巨量,和我以前接触的技术所需要的知识量完全不在一个水平。花了一个月时间,我可能只学到了点皮毛,但是值得庆幸的是,我的这个想法已经将最基本的框架搭建好了。接下来的事情就只有无尽的学习,将这个研究做到成熟,完美。