0


【flask开启进程,前端内容图片化并转pdf-会议签到补充】

flask开启进程,前端内容图片化并转pdf-会议签到补充

用到了pdf,来回数据转发和合成,担心flask卡顿,响应差,于是刚好看到threading方面的介绍,下面详细介绍这两个方面,难点是异步方法的衔接.通过此文期望
让读者对Promise的使用,aysc方法的定义,await,和then的含义有所得.然后可以以此为关键词,解决工作中的一些问题.

flask及flask-socketio开启threading

socketio的开启方法有所差别,不是app中threading=True而是

  1. socketio = SocketIO(app, async_mode='threading',cors_allowed_origins='*' ,message_queue= app.config['REDIS_URL'])

重新启用WERKZEUG 产生了 ‘WERKZEUG_SERVER_FD’ error,从网上发现,很多werkzeug版本出现过这个问题,而且建议回到2.0.0,可以解决,也不管是什么应用了.其中一个是notebook.我pip list了一下目前版本,3.0.3和flask一个版本.显然回去是不可能的.专门搜素WERKZEUG 和这个错误,在他的官方issu中
https://github.com/pallets/werkzeug/issues/2368

  1. thanks for the hint. Apparently, it was setin systemd service file, removing that solved the issue.
  2. # required to disable Flask debugging banner: "Serving Flask app (lazy loading) ..."Environment="WERKZEUG_RUN_MAIN=true"

这是作为独立服务的吧.于是我抱着试试看的心里,在flask的配置中加入了这个选项.我想flask的官方文档肯定有相关的说明.而找起来,看似找起来不是太方便.于是瞎猜

  1. app.config['WERKZEUG_RUN_MAIN']=True # async_mode='threading',

再去启动,不再报错.
测试了gunicorn也正常.到此,虽然flask-socketio限制了worker只能是1,.这样起码threading用起来,会顺畅不少.另外 soketIO这个库,可以启用多worker模式,做一些相关配置.在它的官方说明有说.而且必定可以作为flask,扩展.因为官网也说了.只是目前用起来flask-socketio,不再去测,而且soketIO可以工作的在三种模式,websocket,polling-long.和***忘记了.这个话题暂时到这,下面说转换页面内容,到可打印,而不是可复用.因为是图片,应用性很差.

页面内容转图片转pdf

签到表上次的签到表打印用到了datatable和vue,我这次实现打印的时候, 转到了jquery 操作bootstrap grid.当我费劲把内容呈现在表格样的grid网格里的时候,我发现照样不能打印 说是响应式内容,没有打印风格,于是上路折腾到能打印.中途真想换table. 因为数据从竖排,硬是弄成横竖转换,扯成平的,其实是按照竖向走的,相当于中国古书的样式.从左向右排列,中间还无序分组.12一组,234一组,567一组.每组一个表头.
147
258
369
排列到,一行数据147,一个序列,三个序列.
这个不是重点,只是,完事以后,发现,无法打印.

流程

文件结构组织.
main.html, --含有iframe 和导航
tppint.html- 打印的html原始数据,靠js加载,含有主业务代码,和转换代码
app.py,- 服务器接受图片,转换成pdf,发送回去

  1. Created with Raphaël 2.3.0
  2. main开始,含iframe,src=tpprint
  3. tprpint onload加载布局
  4. tprpint $get页面数据
  5. tprpint toPng 转图片
  6. 图片格式正常
  7. tprpint fetch post图片 return pdf
  8. pdf获取正常
  9. 显示可打印的pdf结束
  10. yes
  11. no
  12. yes
  13. no

前端主js代码

在tpprint.html中的处理toPng,和fetch api/putpng2pdf的异步请求代码,指望的main.html调用.结果在自己 onload中,数据加载末尾$.get,处理的结束调用了,我不知道如何捕获他的结束.也许可以重构成返回Promise的函数.很费事的.填空了一下.

  1. window.asyncprt=asyncfunctionasyncprt(){// 模拟异步操作,如网络请求returnnewPromise((resolve, reject)=>{var node=document.getElementById('main')
  2. domtoimage.toPng(node,{ quality:1}).then(asyncfunction(dataUrl){const response =awaitfetch('./api/putpng2pdf',{ method:'POST', body:JSON.stringify({ dataurl: dataUrl}),
  3. headers:{'Content-Type':'application/json'}});const arrayBuffer=await response.arrayBuffer()const blob =newBlob([arrayBuffer],{ type:'application/pdf'});// 创建一个URL指向Blob对象const blobUrl =URL.createObjectURL(blob);// 打开新的标签页并导航到PDF文件的URL
  4. window.open(blobUrl,"_self");resolve('Function finished!');// window.location.href= "/img/tp.pdf?t="+ Math.floor(Math.random() * 1000);// console.log( r)});});};

在文件末尾是,注释掉的,pdf文件暂存,server硬盘,然后,结束来取.后改成,略过存文件,只是一次性,实时的. 直接flask,BytesIO内存托转.回来一个附件是pdf的流.下面是两方面的细节
如果在用await 调用 一个返回Promise的ayse函数,那么在得到 resolve(‘Function finished!’);语句后,你的调用程序,才会继续执行下去.这是多线程同步的一个通信机制.本来我想在frame外的main.html使用,然后,因为onload,$get没有明显的结束渲染数据的节点,改成了现在这个嵌套引用.

内容转图片-browser端

上次的签到表转图片用的dom-to-image是6年前的老项目,知道他可以纯js引用,他网站有说明.

  1. var node=document.getElementById('main')
  2. domtoimage.toPng(node,{ quality:1}).then(async function(dataUrl){})

可以换toJpeg,也是一样的,数据大一倍.png看似好一点,这个dataurl是base64编码.方便post参数传输,还有一个是blob,方便本地呈现,就和pdf呈现一样.在处理回来数据里有体现blob的处置方式.

browser端的同步编程

涉及到,数据渲染,和图片数据post给后端,有三个事件,分先后.1,onload,2, $.get 取到数据 并给页面赋值 3,.fetch succeed putpng2pdf.将图片dataurl post json给flask API并取得,回程的pdf 附件信息.
上面的[前端主js代码]章节ayncprt是其中的 第三步
第二步末尾,调用asyncprt,我发现 permisA.then(asyncprt)这样是不行的,大概,因为get返回后就已经退出可then了.还有就是,get可能会执行两次.所以设置了一个!done,不一定对的.

  1. var permisA= $.get("/api/gettp",function(data){
  2. stas=JSON.parse(data )if(! done )
  3. Object.keys( stas).forEach(function(key){}asyncprt()})

传参数还不太会,所以就先这样.
第一步的结束,也就是.页面静态内容加载完成,就可以执行第二步.

  1. $().ready(()=>{
  2. $.get("/api/gettp")..................
  1. 虽然以上是执行的反顺序.但是可以做为编码的顺序

因为像上传,转换这样的事情,前后端反复调试.前端有个按钮,带个控制台,来看输出,静态tpprint.html长这样.

  1. <buttontype="button"class="btn btn-success"onclick='printme()'> 下载打印</button></li><divid="main"><divclass="container"><divclass="row"><divclass="col-sm-12"><h3align='center'>重要会议补充信息登记表</h3><h4align="right"> 签到2号表 日期:<spanid="day"></span></h4></div></div></div><divclass="container"id="gd"></div></div>

有个难点是默认情况,出来的图片,会有偏移.显示一半.因为对于contain对象, css的语法, dom-to-image 已经无法全部掌握. 不怪它,浏览器也不懂怎么打印.所以,最后

让它纯粹一点,为了防止边距问题,让它固定起来,且加入三个依赖.

  1. <link href="/static/bootstrap.min1.css" rel="stylesheet" >
  2. <script type="text/javascript" src="/js/jquery.min.js"></script>
  3. <script type="text/javascript" src="/js/dom-to-image.min.js"></script>
  4. <style>
  5. #main{display: block;width:1274px;height:778px;background-color: #ffffe1;}
  6. </style>

这样大致就能用起来了. 最后,这种嵌套的3步骤顺序之所以会被我整出来.是因为我把它放在 主体中的main.html的iframe下.想在加载,生成图片和pdf后,直接显示出来.在我从iframe外围触发打印按钮时开始, 调用asynprt.结果.只有页面标题,不得已,反复想. 然后发现,在

  1. g
  2. e
  3. t
  4. 完成数据后把
  5. a
  6. s
  7. y
  8. n
  9. p
  10. r
  11. t
  12. 放入
  13. .
  14. 只要
  15. i
  16. f
  17. r
  18. a
  19. m
  20. e
  21. 加载全部完成
  22. ,
  23. 就能得到图片
  24. .
  25. m
  26. a
  27. i
  28. n
  29. .
  30. h
  31. t
  32. m
  33. l
  34. 无法定位
  35. i
  36. f
  37. r
  38. a
  39. m
  40. e
  41. t
  42. p
  43. p
  44. r
  45. i
  46. n
  47. t
  48. 全部完成的时间点
  49. .
  50. i
  51. f
  52. r
  53. a
  54. m
  55. e
  56. W
  57. i
  58. n
  59. d
  60. o
  61. w
  62. .
  63. o
  64. n
  65. l
  66. o
  67. a
  68. d
  69. 他是表示前置静态内容结束
  70. .
  71. 不是
  72. j
  73. q
  74. u
  75. e
  76. r
  77. y
  78. get完成数据后把asynprt放入.只要iframe加载全部完成,就能得到图片.在main.html无法定位iframetpprint全部完成的时间点. iframeWindow.onload 他是表示前置静态内容结束.不是jquery
  79. get完成数据后把asynprt放入.只要iframe加载全部完成,就能得到图片.在main.html无法定位iframetpprint全部完成的时间点.iframeWindow.onload他是表示前置静态内容结束.不是jquery的.get, 也不是get 里的data. 结束布局.所以外部根本不知道, 替代方案interval(function(),timesec).去间隔查询主体某个元素.这样很笨拙的 操作.

以下是main,html的内容

  1. <h3 align="center">打印</h3><button onclick="test()"></button><script>
  2. async functiontest(){
  3. var iframeWindow = document.getElementById('myif');
  4. iframeWindow.contentWindow.location.href="./tpiframe.html"
  5. iframeWindow.onload = async ()=>{}};</script><iframe id="myif"src='/tpprint.html'style="display: ; width:1300px;height:800px"></iframe>

最终fetch(‘/api/putpng2pdf’)结束有返回pdf附件后,更新整体 页面

  1. window.open(blobUrl,"_self");

就在main.html页的iframe中 使用服务器的返回pdf,加载为打印窗口,显示结果如下.
在这里插入图片描述
在iframe所处的main,加入导航是个挺好的办法. ,直接出现打印窗户有点别扭那就没有导航了.
不可避免的是,iframe完成加载的会闪烁到pdf. 因为必须呈现才有图片,有图片才能转pdf.
接下来说flask的后台,实现转换和return send_file

flask的主要功能route,def

依赖 img2pdf,比较完美,什么也不用管,一个covert搞定.要说的是,使用BytesIO,以及,def的return send_file.以及base64. covert可以接受bytes.file_path 或file指针这三种参数.

  1. from flask import send_file
  2. import img2pdf
  3. from io import BytesIO
  4. @app.route("/api/putpng2pdf",methods=['POST'])defputpng2pdf():
  5. data = request.get_json()print()import base64
  6. dataraw=base64.b64decode(data['dataurl'][22:])
  7. f_io=png2pdf(dataraw)
  8. f_io.seek(0)return send_file(f_io, as_attachment=True,download_name='tp.pdf',mimetype='application/pdf')defpng2pdf(inraw):
  9. bytes_io = BytesIO(inraw)
  10. out_io= BytesIO()print(len(inraw))#"../img/tp.pdf"# with open(filename ,'wb') as out:
  11. out_io.write(img2pdf.convert(bytes_io))return out_io

这里定义png2pdf参数字节数组,返回,BytesIO

  1. out_io.write(img2pdf.convert(bytes_io))

其它:

  1. 取post参数,发送方式是json,可以request.get_json()这样取得. 还有一种方式是form-data.在 p o s t , 或 post ,或 post,或.ajax,或fetch中有contentype参数定义.
  2. 在flask的route处理中,base64要把头部去掉,只留纯编码串,经过一次decode,得到原始数据,
  3. 送给png2pdf.
  4. 从pdf,字节bytesIO对象,生成returnsend_file(f_io,as_attachment=True,download_name='tp.pdf',mimetype='application/pdf'),这是直接变成pdf数据流的方式. 以pdf附件方式发送http回应,因为f_io是缓冲区BytesIO内存,模仿文件的对象.所以运行提升必须有download_name参数指定名字. 可见send_file可以发送文件,对象, 字符串文件名.
  5. 这就到了前端处理了 在收到回应后浏览器的任务是显示pdf.体现在 tpprint.html的fetch url的处理中如下:
  1. const arrayBuffer=await response.arrayBuffer()const blob =newBlob([arrayBuffer],{ type:'application/pdf'});// 创建一个URL指向Blob对象const blobUrl =URL.createObjectURL(blob);// 打开新的标签页并导航到PDF文件的URL
  2. window.open(blobUrl,"_self");

大概await就相当于then所以,等待respone.arraryBuffer()然后继续得到Blob.一个浏览器可显示的字节包装类.png和jpeg应该也都有这种方式,这里是pdf,dom-to-image也能转化成blob
这一大段都是baiduAI问答提供的,相当准确,还有注释. 只有提问,js get pdf and show .凑一些词语给一些答案. 犹如复杂的事变条理.绝不会出现我这样的行文.
将来我这样的混乱文笔会是个稀罕玩意.主打一个缭乱.

总结

本文的两个问题,都不大,开始threading和 响应式页面,转打印.其中第一个问题需要,设定参数值,不然报错.第二个问题,很难有系统化的解决方法.都是在试错中前进. 也许有成熟的方法.但是都是成体系,成套路的.这里复述一次页面内容转pdf打印界面的文件结构组织.
main.html, --含有iframe 和导航
tppint.html- 打印的html原始数据,靠js加载,含有主业务代码,和转换代码,
app.py,- 服务器接受图片,转换成pdf,发送回去
然后我就转去画流程图了.画完用了很久,大小朋友,有需求,可以想想,那个流程图.
完毕.

标签: flask pdf python

本文转载自: https://blog.csdn.net/wjcroom/article/details/143548767
版权归原作者 wjcroom 所有, 如有侵权,请联系我们删除。

“【flask开启进程,前端内容图片化并转pdf-会议签到补充】”的评论:

还没有评论