这是我参与2022首次更文挑战的第30天,活动详情查看2022首次更文挑战
前情提要:
最近项目有需求需要将富文本输入框的内容转成图片给app显示, 从前端拿到了html格式str,网上找到了imgkit这个库,真心十分强大,作者也很好(华裔/国人), 支持将html文件 str 和url转成图片或者pdf,网上很多例子就不赘述了。
subprocess 以及stdin,stdout和stderr
这里先简单提一下subprocess,subprocess是python2.4中新增的一个模块,主要用于创建子进程,连接他们的输入input、输出output和错误error管道,并获取他们的返回状态(码)。下面简单说一下他的三个参数stdin stdout 和 stderr
stdin,stdout和stderr:
子进程的标准输入,输出和错误.值可以是subprocess.PIPE、subprocess.DEVNULL、一个已经存在的文件描述符、已经打开的文件对象或者 None。
subprocess.PIPE表示为子进程创建新的管道,subprocess.DEVNULL表示使用os.devnull。默认使用的是 None,表示什么都不做。另外,stderr 可以合并到 stdout 里一起输出。
问题:
我在本地和Django中跑都没有问题,但是放在iis 中就报错 OSError: [WinError 6] 句柄无效,找了一圈也没找到原因,遂去作者的github 写了issues 没想到得到了作者的回复并修复了, github.com/jarrekk/img… github.com/jarrekk/img… 源码没有处理错误输出遂自己增加了
错误日志:
File ".\Plugin\imgkit.py", line 25, in generate_img_html
cfg = imgkit.config(wkhtmltoimage=path_wkimg)
File "c:\api\venv\lib\site-packages\imgkit\api.py", line 101, in config
return Config(**kwargs)
File "c:\api\venv\lib\site-packages\imgkit\config.py", line 24, in __init__
stdout=subprocess.PIPE).communicate()[0].strip()
File "C:\Users\Administrator\AppData\Local\Programs\Python\Python36\lib\subprocess.py", line 665, in __init__
errread, errwrite) = self._get_handles(stdin, stdout, stderr)
File "C:\Users\Administrator\AppData\Local\Programs\Python\Python36\lib\subprocess.py", line 919, in _get_handles
errwrite = _winapi.GetStdHandle(_winapi.STD_ERROR_HANDLE)
OSError: [WinError 6] 句柄无效。
修改源码:
修改imgkit/congfig.py 24 行 stdout=subprocess.PIPE, stderr=subprocess.STDOUT, stdin=subprocess.DEVNULL 附上config.py 原因猜测是django运行在iis中进程输出有问题
# -*- coding: utf-8 -*-
import subprocess
import sys
class Config(object):
def __init__(self, wkhtmltoimage='', meta_tag_prefix='imgkit-'):
self.meta_tag_prefix = meta_tag_prefix
self.wkhtmltoimage = wkhtmltoimage
self.xvfb = ''
if not self.wkhtmltoimage:
if sys.platform == 'win32':
self.wkhtmltoimage = subprocess.Popen(['where', 'wkhtmltoimage'],
stdout=subprocess.PIPE, stderr=subprocess.STDOUT, stdin=subprocess.DEVNULL).communicate()[0].strip()
else:
self.wkhtmltoimage = subprocess.Popen(['which', 'wkhtmltoimage'],
stdout=subprocess.PIPE, stderr=subprocess.STDOUT, stdin=subprocess.DEVNULL).communicate()[0].strip()
if not self.xvfb:
if sys.platform == 'win32':
self.xvfb = subprocess.Popen(['where', 'xvfb-run'],
stdout=subprocess.PIPE, stderr=subprocess.STDOUT, stdin=subprocess.DEVNULL).communicate()[0].strip()
else:
self.xvfb = subprocess.Popen(['which', 'xvfb-run'],
stdout=subprocess.PIPE, stderr=subprocess.STDOUT, stdin=subprocess.DEVNULL).communicate()[0].strip()
try:
with open(self.wkhtmltoimage):
pass
except IOError:
raise IOError('No wkhtmltoimage executable found: "{0}"\n'
'If this file exists please check that this process can '
'read it. Otherwise please install wkhtmltopdf - '
'http://wkhtmltopdf.org\n'.format(self.wkhtmltoimage))