為什么Django FieldFIle Readline()返回十六進制版本的文本文件? [英] Why is Django FieldFIle readline() returning the hex version of a text file?
問題描述
有奇怪的問題。
我有一個Django應用程序,它打開一個文件(表示為DjangoFieldFile
),并使用readline()
讀取每一行,如下所示:
with file.open(mode='r') as f:
row = f.readline()
# do something with row...
該文件為文本,采用UTF-8編碼,行以
結尾。
問題是每一行都被讀取為字符串的十六進制表示形式,因此我得到的不是"Hello",而是"48656c6c6f"。
一些奇怪的事情:
它以前工作正常,但在某個時候被更新破壞了(我嘗試回滾到以前的提交,但它仍然不穩定,所以可能是依賴項已經更新,而不是我的
requirements.txt
中的內容)。在我的測試中遺漏了它,因為它位于應用程序中很少使用的部分。如果我使用
readlines()
而不是readline()
讀取相同的文件,我會看到[b'...']
中包含的文件的正確字符串表示形式
如果我直接使用Python
open()
和readline()
從解釋器讀取文件,則文件可以正常讀取使用
mode='rt'
強制文本模式不會改變行為,mode='rb'
文件存儲在mini存儲桶中,因此默認存儲是
storages.backends.s3boto3.S3Boto3Storage
fromdjango-storages
,而不是默認的Django存儲類。這意味著boto3
、botocore
和s3fs
也混合在一起,這給我的調試帶來了更大的困惑。
抓撓我的頭,不知道為什么以前這樣做有效,我做錯了什么。
環境是在Docker容器中運行的Python3.8、Django 2.2.8和3.0(結果相同)。
編輯
讓我指出,解決此問題的方法只需使用
row = f.readline().decode()
但我還是想弄清楚發生了什么事。
編輯%2
此外,FieldFile.open()將文件讀取為二進制文件,而普通的Python Open()將文件讀取為文本文件。
推薦答案
這似乎非常奇怪。 我想你在嘗試下面的答案后會立即看到解決方案(如果確實無濟于事,我會更新我的答案或刪除它,但我很有信心)
假設有一些代碼,即monkeypatting file.open或Django視圖函數。
我的建議是:
從made.py runserver開始您的代碼 將以下代碼添加到made.py(作為第一行)import file
print("ID of file.open at manage startup is", id(file.open)
然后將代碼直接添加到視圖file.open
上方的一行
print("ID of file.open before opening is", id(file.open)
如果這兩個ID不同,那么一定有什么東西篡改了您的打開函數。 如果兩者相同,則問題一定出在其他地方。
如果您沒有看到這兩個打印的輸出,則可能是某些東西破壞了您的視圖。
如果這不起作用,則嘗試使用open()
而不是file.open()
您使用file.open()
附錄1:
那么您所說的是,該文件是一個類的對象實例,它是一個Filefield嗎? 在任何情況下,您都可以獲取文件的名稱并使用正常的open()
打開它,以查看是只有file.open()
才做有趣的事情,還是open()
也是以這種奇怪的方式讀取它。
您是使用cat filename
從命令行打開文件(還是使用type filename
在Windows下打開文件?
如果這不起作用,我們可以在正在執行的源代碼的每一行后面添加跟蹤。
附錄2:
如果您無法在manage.py runserver
中嘗試此操作,如果您嘗試使用manage.py shell
讀取文件,會發生什么情況?
只需打開外殼并鍵入類似以下內容:
from <your_application>.models import <YourModel>
entry = <YourModel>.objects.get(id=<idofentry>)
line1 = entry.<filefieldname>.open("r").read().split("
")[0]
print("line1 = %r" % line1)
如果仍不確定(但僅當您可以用命令行管理程序重現該問題時),則創建一個包含行的小文件。
from <your_application>.models import <YourModel>
entry = <YourModel>.objects.get(id=<idofentry>)
import pdb; pdb.set_trace()
line1 = entry.<filefieldname>.open("r").read().split("
")[0]
print("line1 = %r" % line1)
并從命令行管理程序導入它。 代碼應該進入調試器,現在您可以單步通過打開函數,并查看您是否在某個monkeypatch中使用了類似的奇怪函數。
這篇關于為什么Django FieldFIle Readline()返回十六進制版本的文本文件?的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,也希望大家多多支持IT屋!