UnicodeEncodeError: 'ascii'编解码器无法编码字符

37

当上传带有非ASCII字符的文件时,我会收到UnicodeEncodeError错误:

Exception Type: UnicodeEncodeError at /admin/studio/newsitem/add/
Exception Value: 'ascii' codec can't encode character u'\xf8' in position 78: ordinal not in range(128)

查看完整的堆栈跟踪

我使用MySQL、nginx和FastCGI运行Django 1.2。

这个问题在Django Trac数据库中已经修复,但我仍然遇到了这个问题。欢迎提出任何修复建议。

编辑:这是我的图像字段:

image = models.ImageField(_('image'), upload_to='uploads/images', max_length=100)

你能给出模型/字段的定义吗?我特别想看一下upload_to的定义。 - Mark Lavin
已更新上传定义。 - vorpyg
2
对于仍然来到这里的任何人,请查看akaihola在Django票证上的最后一条评论,他说:“Debian默认使用LANG=C语言环境运行Apache,这会导致在使用mod_wsgi运行时上传名称中带有特殊字符的文件时出现问题。在/etc/apache2/envvars中激活UTF-8语言环境应该可以解决这个问题。” 票证链接:http://code.djangoproject.com/ticket/6009 - Tuukka Mustonen
1
这同样适用于nginx。在这里检查我的答案:https://dev59.com/K3A65IYBdhLWcg3wogIb#7602446 - vorpyg
12个回答

41

对于在使用Supervisor运行Django时遇到此问题的任何人,解决方法是将以下内容添加到Supervisor配置文件中supervisord部分:

environment=LANG="en_US.utf8", LC_ALL="en_US.UTF-8", LC_LANG="en_US.UTF-8"

这对我在运行于Debian Squeeze的Supervisor 3.0a8中解决了问题。

同时确保通过运行以下命令,Supervisor重新读取配置:

supervisorctl reread
supervisorctl restart myservice

(感谢@Udi)


对于upstart,在您的/etc/init/myservice.conf中添加:

env LANG="en_US.utf8"
env LC_ALL="en_US.UTF-8"
env LC_LANG="en_US.UTF-8"`

(感谢 @Andrii Zarubin;有关更多信息,请参阅Upstart文档中的环境变量)


6
确保执行 /etc/init.d/supervisor stop 和 /etc/init.d/supervisor start 命令,才能使更改生效。仅仅重新启动不起作用。 - amjoconn
如果你遇到了“意外的键/值对结束”错误,那么你需要给值加上引号。例如:environment=LANG='en_US.utf8'。https://lists.supervisord.org/pipermail/supervisor-users/2010-March/000539.html - amos
2
您可以使用 supervisorctl rereadsupervisorctl restart myservice 命令来强制读取配置文件,而无需停止和启动整个守护进程。 - Udi
2
很遗憾我不能给这个答案点赞多一些,你真的救了我的星期天,谢谢! - lithiium
1
如果您正在使用upstart - 您必须添加 env LANG="en_US.utf8" env LC_ALL="en_US.UTF-8" env LC_LANG="en_US.UTF-8" - Andrii Zarubin
显示剩余4条评论

24
在只能接受ASCII编码(例如控制台或路径)的地方展示Unicode字符串时,您需要告诉Python尽最大努力替换非ASCII字符。
>> problem_str = u'This is not all ascii\xf8 man'
>> safe_str = problem_str.encode('ascii', 'ignore')
>> safe_str
'This is not all ascii man'

在Django的模板中,小心地处理可以避免管理后台中出现编码问题。但是,如果您添加了自定义列并忘记将值转换为ASCII,或者覆盖了模型的str方法并忘记执行此操作,就会遇到相同的错误,从而阻止模板渲染。

如果该字符串被保存到您的(希望是UTF-8)数据库中,则不会有问题。看起来,您正在尝试上传一个使用具有非ASCII字符的实体标题的文件。


谢谢!在一次简单问题的徒劳搜索之后,我偶然发现了这个答案:如何在Python中使用非拉丁字符发送电子邮件?你的解决方案可行! - skanatek

14

希望这可以帮到您。在我的情况下,我正在通过daemontools运行Django。

设置

export LANG='en_US.UTF-8'
export LC_ALL='en_US.UTF-8'

在执行manage.py之前运行脚本解决了上传文件名的问题。


12

经过进一步调查,我发现我没有在我的主要Nginx配置文件中设置字符集:

http {
  charset  utf-8;
}

通过添加上述内容,问题消失了,我认为这是处理此问题的正确方式。


4
只有当nginx直接运行后端代码时才能起作用。假设它是像gunicorn或uwsgi这样的代理,您将需要配置wsgi服务器的环境以使用UTF-8。将此添加到您的Nginx配置中不会有任何危害,但它可能无法解决您的问题。 - amjoconn
正如@amjoconn所提到的,在我的情况下,问题是通过在我的uwsgi配置文件中添加“env = LC_ALL=ru_RU.UTF-8”来解决的。 - Vasiliy Toporov

11

akaihola的回答很有帮助。对于那些使用upstart脚本管理uWSGI运行的Django应用程序的人,只需要将这些行添加到您的/etc/init/yourapp.conf文件中:

env LANG="en_US.utf8"
env LC_ALL="en_US.UTF-8"
env LC_LANG="en_US.UTF-8"

它为我解决了这个问题。


3
谢谢!这就是解决我的问题的方法!env LANG="en_US.UTF-8" env LC_ALL="en_US.UTF-8" env LC_LANG="en_US.UTF-8"。注意这里使用的是 env 而不是 export,这是在 System V 脚本(/etc/init/xxx.conf)下使用的语法。这个错误浪费了我多小时的时间。 - moonkey

4
如前所述,这与语言环境有关。例如,如果您使用gunicorn来提供服务django应用程序,则可能有一个init.d脚本(或像我一样的运行脚本),在其中您可以设置语言环境。

要解决文件上传中的UnicodeEncodeError,请在运行应用程序的脚本中加入类似于export LC_ALL=en_US.UTF8的内容。

例如,这是我的脚本(使用gunicornrunit):

#!/bin/bash
export LC_ALL=en_US.UTF8
cd /path/to/app/projectname
exec gunicorn_django -b localhost:8000 --workers=2

此外,您可以在视图中使用以下内容检查模板的区域设置:

import locale
data_to_tpl = {'loc': locale.getlocale(), 'lod_def': locale.getdefaultlocale()}

只需在模板中显示{{loc}} - {{loc_def}}即可。

您将获得有关语言环境设置的更多信息!这对我非常有用。


4
没有看到更多的代码很难说,但这似乎与这个问题有关:UnicodeDecodeError on attempt to save file through django default filebased backend

查看 Django 提交中提到的内容,应该遵循类似于“如果您遇到 UnicodeEncodeError”的部署文档:
https://docs.djangoproject.com/en/1.4/howto/deployment/modpython/#if-you-get-a-unicodeencodeerror

(我知道这是针对 Apache/mod_python 的,但我猜测在使用 nginx 时,根本问题是文件系统编码不是 UTF-8,而且使用类似的修复方法)
编辑: 据我所知,这个 nginx 模块将是等效的修复方法:http://wiki.nginx.org/NginxHttpCharsetModule

我怀疑这可能与此有关。我尝试按照这里描述的方式在字符串前面添加一个u:https://dev59.com/questions/X0zSa4cB1Zd3GeqPn5vt#2458200,但没有成功。你有nginx修复链接吗? - vorpyg
谢谢,虽然还是不起作用。我已经尝试了根据Django文档中指示设置区域设置,并且还尝试在我的nginx配置中添加了utf8字符集。也许我只需要重写保存方法来先重命名文件... - vorpyg
文档链接已失效。 - Medeiros

4
另一个有用的选项是更改 Python 的默认编码,避免重写代码。
如果您正在使用 virtualenv,您可以更改(或创建,如果不存在)env/lib/python2.7/sitecustomize.py 并添加以下内容:
import sys
sys.setdefaultencoding('utf-8')

或者,如果您在生产系统中,您可以对/usr/lib/python2.7/sitecustomize.py执行相同的操作。

3

在其他线程的基础上,我想进一步提供帮助...

我也遇到了一个问题,当我上传带有非ASCII字符的文件名时,genericpath.py会出现UnicodeEncodeError错误。

我使用的是nginx,uwsgi和带有Python 2.7的Django。

本地环境一切正常,但服务器上不行。

以下是我采取的步骤: 1. 在 /etc/nginx/nginx.conf 中添加(没有解决问题)

http {
    charset utf-8;
}
  1. I added this line to etc/default/locale (did not fix the problem)

    LANGUAGE="en_US.UTF-8"

  2. I followed the instructions here listed under the heading 'Success' https://code.djangoproject.com/wiki/ExpectedTestFailures (did not fix the problem)

    aptitude install language-pack-en-base
    
  3. Found across this ticket https://code.djangoproject.com/ticket/17816 which suggested testing a view on the server to what was happening with locale information

在你的看法中
import locale
locales = "Current locale: %s %s -- Default locale: %s %s" % (locale.getlocale() + locale.getdefaultlocale())

在你的模板中
{{ locales }}

对我来说,问题在于我的Ubuntu服务器没有地区设置和默认地区设置(尽管我的本地OSX开发机器上有),所以文件名/路径中含有非ASCII字符的文件将无法正确上传,Python会引发UnicodeEncodeError,但这只会在生产服务器上发生。

解决方案

我在我的网站和网站管理uwsgi配置文件中都添加了以下内容,例如/etc/uwsgi-emperor/vassals/my-site-config-ini文件。

env = LANG=en_US.utf8

你如何将此添加到Apache2站点配置中? - Ebram Shehata

3
如果您正在使用Django和Python 2.7,则以下内容对我有用:
@python_2_unicode_compatible
class Utente(models.Model):

请查看{{link1}}


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接