Pickle 数据是不透明的二进制数据,即使您使用协议版本 0:
>>> pickle.dumps(data, 0)
'(dp0\nI1\nV\xe9\np1\ns.'
当您尝试将其存储在 a 中TextField
时,Django 将尝试将该数据解码为 UTF8 以存储它;这就是失败的原因,因为这不是 UTF-8 编码的数据;它是二进制数据:
>>> pickled_data.decode('utf8')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/Users/mj/Development/venvs/stackoverflow-2.7/lib/python2.7/encodings/utf_8.py", line 16, in decode
return codecs.utf_8_decode(input, errors, True)
UnicodeDecodeError: 'utf8' codec can't decode byte 0xe9 in position 9: invalid continuation byte
解决方案是不要尝试将其存储在TextField
. 使用 aBinaryField
代替:
存储原始二进制数据的字段。它只支持bytes
赋值。请注意,此字段的功能有限。例如,无法根据 BinaryField 值过滤查询集。
您有一个bytes
值(Python 2 字符串是字节字符串,bytes
在 Python 3 中重命名为)。
如果您坚持将数据存储在文本字段中,请将其显式解码为latin1
; Latin 1 编解码器将字节一对一地映射到 Unicode 代码点:
>>> pickled_data.decode('latin1')
u'(dp0\nI1\nV\xe9\np1\ns.'
并确保在再次 unpickling 之前再次对其进行编码:
>>> encoded = pickled_data.decode('latin1')
>>> pickle.loads(encoded)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/Users/mj/Development/Libraries/buildout.python/parts/opt/lib/python2.7/pickle.py", line 1381, in loads
file = StringIO(str)
UnicodeEncodeError: 'ascii' codec can't encode character u'\xe9' in position 9: ordinal not in range(128)
>>> pickle.loads(encoded.encode('latin1'))
{1: u'\xe9'}
请注意,如果您让该值进入浏览器并在文本字段中再次返回,则浏览器可能已经替换了该数据中的字符。例如, Internet Explorer 会将\n
字符替换\r\n
为 ,因为它假定它正在处理文本。
并不是说您在任何情况下都应该允许从网络连接中接受 pickle 数据,因为这是一个等待利用的安全漏洞。