[求大神解答] Django 项目部署到线上后出现编码问题 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
watermelon92
V2EX    Python

[求大神解答] Django 项目部署到线上后出现编码问题

  •  
  •   watermelon92 2018-06-26 12:03:49 +08:00 3392 次点击
    这是一个创建于 2678 天前的主题,其中的信息可能已经有所发展或是发生改变。

    一、项目说明:

    用户上传一个压缩文件(内含 html 文件夹),网站解压文件并保存至指定目录,供外部直接访问静态 html 文件。

    二、问题:

    解压文件时报错:UnicodeEncodeError: 'ascii' codec can't encode characters,具体信息见链接:报错信息

    三、尝试思路

    1. 代码在本地,macos 下可解压含中文的文件。
    2. 在线上解压英文 html 文件也是正常的。
    3. 报错的代码单独在 ubuntu 下运行正常,可以创建文件。
    4. google 了所有关键词,基本都是 2.7 的解决方案,在项目里使用无效。
    5. 找到一个类似的问题,可能由 ubuntu 系统语言原因导致。原问题链接

    四、出错代码块:

    def handle_uploaded_file(f, num, new_dir_name): release_file_dir = os.path.join(UPLOAD_FILES_DIR, str(num)) new_dir = os.path.join(release_file_dir,new_dir_name) filePath = f with zipfile.ZipFile(filePath, 'r') as zf: for fn in zf.namelist(): right_fn = fn.encode('cp437').decode('utf-8') # 将文件名正确编码 right_fn = os.path.join(release_file_dir, right_fn) if right_fn[-1] == '/': os.makedirs(right_fn, mode=0o777) #创建文件夹 continue with codecs.open(right_fn, 'w+', encoding='utf-8') as output_file: # 创建并打开新文件 with zf.open(fn, 'r') as origin_file: # 打开原文件 shutil.copyfileobj(origin_file, output_file) # 将原文件内容复制到新文件 renameFile(release_file_dir,new_dir) return os.path.join(release_file_dir,new_dir_name)``` 
    第 1 条附言    2018-07-02 19:11:48 +08:00
    感谢各位大佬的帮助,问题已解决。现贴出解决方案供后来者参考:
    确认问题:
    是运行环境的默认编码问题,尽管在 shell 中打印出环境是 utf-8 解码,但是项目中依旧是 ascii.

    解决方案:
    服务器中配置环境参数:envirOnment=LANG=en_US.UTF-8,LC_ALL=en_US.UTF-8
    如果是用的 uWSGI:
    可参考该链接说明配置: https://docs.djangoproject.com/en/dev/howto/deployment/wsgi/uwsgi/
    如果是用的 gunicorn:
    可使用 superviosr 来管理进程,并在 supervisor 中配置上述环境参数。

    再次感谢大佬们帮助。
    36 条回复    2018-06-27 12:22:17 +08:00
    golmic
        1
    golmic  
       2018-06-26 12:42:25 +08:00 via Android
    服务器的 python 版本是? Python 解压用的哪个库?我没理解错需求吧,就是程序解压 zip 的时候出的错
    watermelon92
        2
    watermelon92  
    OP
       2018-06-26 12:48:59 +08:00
    @golmic python3.4 解压用的 zipfile
    1130335361
        3
    1130335361  
       2018-06-26 12:55:46 +08:00
    用的是 apache 还是 nginx,我之前 django 配合 apache 的时候出现过这个问题
    watermelon92
        4
    watermelon92  
    OP
       2018-06-26 12:57:44 +08:00
    @1130335361 用的 Nginx 加 gunicorn,你之前也是到服务器上才报错吗?
    golmic
        5
    golmic  
       2018-06-26 13:02:00 +08:00 via Android   1
    服务器上手动跑一下解压的函数,贴一下报错信息
    1130335361
        6
    1130335361  
       2018-06-26 13:10:14 +08:00
    @watermelon92 是的,改了 apache 的的环境变量才行的
    1130335361
        8
    1130335361  
       2018-06-26 13:16:19 +08:00   1
    @watermelon92 拉到最底部有说“ Fixing UnicodeEncodeError for file uploads ”这个错误的解决办法,https://docs.djangoproject.com/en/dev/howto/deployment/wsgi/uwsgi/
    watermelon92
    9
    watermelon92  
    OP
       2018-06-26 13:33:35 +08:00
    @golmic 好的,我稍后试下。谢谢!
    watermelon92
        10
    watermelon92  
    OP
       2018-06-26 13:34:05 +08:00
    @1130335361 我用的是 gunicorn,好像没写过 uwsgi.ini 文件。。
    v1v
        11
    v1v  
       2018-06-26 13:47:46 +08:00   1
    以前 centos 也出现这个问题, 改 /etc/sysconfig/i18n,
    设置 LANG=en_US.UTF-8 , 重启服务器就好了
    watermelon92
        12
    watermelon92  
    OP
       2018-06-26 13:57:44 +08:00
    em...我的 ubuntu 系统没有 sysconfig 文件夹
    watermelon92
        13
    watermelon92  
    OP
       2018-06-26 14:03:29 +08:00
    @v1v 这是现在 locale 的结果:

    LANG=en_US.utf8
    LANGUAGE=
    LC_CTYPE="en_US.utf8"
    LC_NUMERIC="en_US.utf8"
    LC_TIME="en_US.utf8"
    LC_COLLATE="en_US.utf8"
    LC_MOnETARY="en_US.utf8"
    LC_MESSAGES="en_US.utf8"
    LC_PAPER="en_US.utf8"
    LC_NAME="en_US.utf8"
    LC_ADDRESS="en_US.utf8"
    LC_TELEPHOnE="en_US.utf8"
    LC_MEASUREMENT="en_US.utf8"
    LC_IDENTIFICATION="en_US.utf8"
    LC_ALL=
    metamask
        14
    metamask  
       2018-06-26 14:20:11 +08:00
    @watermelon92
    直接去到 21 行 log 下看名字变成了什么;

    确定编码问题后再往上处理

    楼上说的解决办法
    如果是 uwsgi + nginx 这一套,直接再 uwsgi 指定 LANG 就可以了。
    lxy42
        15
    lxy42  
       2018-06-26 14:24:21 +08:00
    right_fn = fn.encode('cp437').decode('utf-8') # 将文件名正确编码
    这里为什么需要对 fn 进行 encode 和 decode ? fn 如果可以 encode 的话,说明 fn 是 unicode 类型,就没必要先 encode 在 decode 了(最终结果还是 unicode ) 而且 encode 和 decode 的编码参数都不一致,可能会导致异常。

    说回你的问题,我猜测是 `right_fn = os.path.join(release_file_dir, right_fn)` 导致`right_fn`编码出问题了,建议你检查一下`release_file_dir` 和 `right_fn ` 的编码。
    watermelon92
        16
    watermelon92  
    OP
       2018-06-26 14:29:42 +08:00
    debug 信息里,文件名是对的。因为 zipfile 解压是默认用‘ cp437 ’ decode 的,所以我先做了解压文件的名称处理。得到正确的文件名后,用 open()函数基于解压后的文件名创建文件的。目前就是提示在这个 open()函数上出错了。但我在服务器端直接运行同样的函数是可以正常创建文件的。
    watermelon92
        17
    watermelon92  
    OP
       2018-06-26 14:32:03 +08:00
    @lxy42
    @freakxx
    两位大佬我在楼上回复了
    watermelon92
        18
    watermelon92  
    OP
       2018-06-26 14:39:07 +08:00
    @lxy42
    @freakxx
    我把 debug 出来的文件名称截图了,见下方链接:
    http://7xuagw.com1.z0.glb.clouddn.com/debug.png
    glasslion
        19
    glasslion  
       2018-06-26 15:05:47 +08:00   1
    @watermelon92
    目前你的写法是要求 zip 包里的所有文件都是 utf-8 编码的。
    既然你目的是复制文件,直接把文件以二进制打开,岂不是更简单?
    metamask
        20
    metamask  
       2018-06-26 15:12:37 +08:00
    $LANG
    metamask
        21
    metamask  
       2018-06-26 15:14:22 +08:00   1
    @watermelon92
    echo $LANG
    然后在 Gunicorn 指定用户为你当前用户
    试下
    lxy42
        22
    lxy42  
       2018-06-26 15:19:12 +08:00   1
    @waermelon92 #18
    right_fn 编码没问题,那可能是文件系统编码默认是 ascii 的问题,你看一下 sys.getfilesystemencoding()是什么。
    如果是 ascii 的话,
    1. 设置正确的 locale
    或者
    2. 将 right_fn 编码为 bytes:
    right_fn = right_fn.encode(ENCODING)
    with codecs.open(right_fn, 'w+', encoding='utf-8') as output_file: # 创建并打开新文件
    Tzen
        23
    Tzen  
       2018-06-26 15:20:23 +08:00
    之前遇到过这个问题,我用 supervisor 管理的进程,配置里更改环境变量就好;
    我项目里是这么加的:envirOnment=LANG=en_US.UTF-8,LC_ALL=en_US.UTF-8
    copie
        24
    copie  
       2018-06-26 15:39:19 +08:00 via Android
    这个属于系统编码的问题。我用 docker 发布的时候如下解决的。


    echo "zh_CN.UTF-8 UTF-8" > /etc/locale.gen
    locale-gen
    ENV LANG=zh_CN.UTF-8
    copie
        25
    copie  
       2018-06-26 15:39:57 +08:00 via Android   1
    这个属于系统编码的问题。我用 docker 发布的时候如下解决的。

    RUN echo "zh_CN.UTF-8 UTF-8" > /etc/locale.gen
    RUN locale-gen
    ENV LANG=zh_CN.UTF-8
    robinlovemaggie
        26
    robinlovemaggie  
       2018-06-26 15:46:04 +08:00   1
    把字符乱码问题归结系统语言设置上是不可取的,这本身不符合 KISS 原则的。
    问题可能处在你的开发环境里“ cp437 ”字符编码不能和目标服务器兼容,处理这部分就好了,放弃修改服务器系统设置。
    metamask
        27
    metamask  
       2018-06-26 15:55:24 +08:00
    @watermelon92
    @copie


    尽量像
    @robinlovemaggie
    说的,
    不要去动系统层面的配置

    如果是用 uwsgi 那么就在里面指定 LANG
    如果是用 supervisor 也在里面像 @Tzen 说的,设置下
    robinlovemaggie
        28
    robinlovemaggie  
       2018-06-26 16:02:36 +08:00
    @freakxx 附议。
    @watermelon92 其实道理很简单,如果是靠更改系统设置解决了问题,那么换一个系统,问题依然存在,等于问题没解决。
    watermelon92
        29
    watermelon92  
    OP
       2018-06-26 16:26:50 +08:00
    @robinlovemaggie
    @freakxx
    谢谢指导。
    cp437 是 zipfile 的标准吧?为啥会不兼容 ubuntu ?而且我打印出来的 log 是显示正常的,楼上有截图。
    robinlovemaggie
        30
    robinlovemaggie  
       2018-06-26 16:35:03 +08:00
    @watermelon92 来自 wiki 关于 codepage437 的解释:
    Code page 437 is the character set of the original IBM PC (personal computer), or MS-DOS. It is also known as CP437, OEM 437, PC-8, or MS-DOS Latin US. The set includes ASCII codes 32 126, extended codes for accented letters (diacritics), some Greek letters, icons, and line-drawing symbols.
    copie
        31
    copie  
       2018-06-26 19:50:12 +08:00 via Android
    @robinlovemaggie 但是通过配置 Python 启动的环境变量确实可以成功运行。但是如果有一天代码上线后出现问题远程打开代码注释都是乱码。log 汉字这是乱码岂不是更加的闹心。
    tlday
        32
    tlday  
       2018-06-27 00:11:42 +08:00 via iPhone   1
    呃,我没看懂你们在说啥,但是,同样是 zip 文件,在 Windows 下和 Unix 下,文件名的编码是有差异的,这个是 zip 文件标准没有覆盖文件名编码方式的问题,应该从这个方向着手。修改系统编码方式是不可取的。
    tlday
        33
    tlday  
       2018-06-27 00:18:02 +08:00 via iPhone
    你的注释打开原文件的这一行好像也没有指定打开文件的编码方式,然后把原文件复制到新文件如果编码不同,是可以直接复制的吗…不需要 decode,encode 吗?我在 python 解决编码问题方面是个菜鸡,只是提供下思路。
    tlday
        34
    tlday  
       2018-06-27 00:29:29 +08:00 via iPhone
    感觉自己没仔细看回复闹了乌龙
    robinlovemaggie
        35
    robinlovemaggie  
       2018-06-27 09:40:51 +08:00
    @copie 远程汉字乱码可以改一下 ssh 工具的传输编码就行了
    td width="10" valign="top">
    watermelon92
        36
    watermelon92  
    OP
       2018-06-27 12:22:17 +08:00
    @golmic
    我在服务器上单独运行了,成功解压没有报错。
    demo 代码见下:
    import os,shutil
    import zipfile

    filePath = '/Users/dylan/Desktop/3.2.zip'
    release_file_dir = '/Users/dylan/Desktop/testunzip3'


    with zipfile.ZipFile(filePath, 'r') as zf:
    for fn in zf.namelist():
    right_fn = fn.encode('cp437').decode('utf-8') # 将文件名正确编码
    right_fn = os.path.join(release_file_dir,right_fn)

    if right_fn[-1]=='/':
    os.makedirs(right_fn, mode=0o777)
    continue

    with open(right_fn, 'wb') as output_file: # 创建并打开新文件
    with zf.open(fn, 'r') as origin_file: # 打开原文件
    shutil.copyfileobj(origin_file, output_file) # 将原文件内容复制到新文件
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2507 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 25ms UTC 14:59 PVG 22:59 LAX 07:59 JFK 10:59
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86