什么时候出现了问题?
之前的文章,里面在用PIL生成验证码图片时,设置字体时一开始这样写的
font=ImageFont.truetype('times.ttf',size=40)
且字体文件和这句代码所在文件都处于同一个目录,理论上引用处于同一目录内的文件时是可以直接使用被引用文件名的。用pycharm在本地启动这个项目,验证码图片正常生成。可是将所有项目文件上传到服务器(腾讯云CentOS),在服务器上用uwsgi启动项目,验证码图片却加载失败了,uwsgi的日志里有报错OSError: cannot open resource,说明字体没有加载成功。要么是从系统中复制出字体文件时手动修改了文件名,要么是加载时路径不对。我是复制系统的Times New Roman 常规,就叫times.ttf,所以是加载时路径不对。
然后换个稍微规范点的写法,这样也不行。
font=ImageFont.truetype('./times.ttf',size=40)
如何解决?
既然相对路径它找不到文件,那只能绝对路径了。绝对路径分两种,一是直接写死,如/xxx/xxx/xxx/times.ttf;另一种是先获取当前代码文件的绝对路径,再根据这个绝对路径拼接出自己需要的路径。这两种绝对路径的方法在服务器都能正常加载出验证码,下面是第二种方法的代码,当然也可以用os.path.join()拼接。为什么不用os.getcwd()而用os.path.abspath(),后面再说。
font=ImageFont.truetype(os.path.abspath(os.path.dirname(__file__))+'/times.ttf',size=40)
如果自己需要的文件,和当前代码文件不在同一个目录呢?可以用上面的写法,在后面拼接/../。这种情况我没在服务器上测试,在本地用命令行测试了,可以取到需要的文件,用命令行的原因后面再说。在代码文件的上一级目录内新建1.txt并写入一些内容,然后运行下面的代码,可以打印出1.txt里的内容。
import os
p=os.path.abspath(os.path.dirname(__file__))
p2=p+'/../1.txt'
with open(p2,'r') as f:
content=f.read()
print(content)
也可以直接用os.path.dirname(os.path.abspath(__file__))获取当前文件的上级目录。
什么原因?
pycharm启动项目和命令行启动时寻找路径的起始路径不一样。
- pycharm是在项目中寻找的;
- 命令行是在当前代码文件上一级目录寻找的,如util.py中需要加载那个字体文件,就从util.py的上一级目录开始找;
- vscode是调用命令行的,但它从哪开始寻找,取决于你在vscode里打开了什么文件夹,它从你打开的文件夹开始找。
可以用下面的代码测试,这里我就用了os.getcwd()
import os
path = os.getcwd().replace('\\','/') + '/'
print(path)
总结
为了代码能跨平台运行,在需要使用路径的地方,特别是引用文件的时候,最好用绝对路径,而不要用相对路径。因为不同的启动项目的方式,找寻文件时的起始目录不同,可能导致找不到要加载的文件。
参考链接1是python导包失败的讲解,和这个问题类似,都是找不到要加载的文件,注意P1、P2都要看。