我繼承了一個 python3 專案,我們試圖用 python 3.5.6 決議一個 70 MB 的檔案。我正在使用 cgi.FieldStorage
我正在嘗試發送的檔案(名為:paketti.ipk):
kissakissakissa
kissakissakissa
kissakissakissa
標題:
X-FILE: /tmp/nginx/0000000001
Host: localhost:8082
Connection: close
Content-Length: 21
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:100.0) Gecko/20100101 Firefox/100.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: multipart/form-data; boundary=---------------------------264635460442698726183359332565
Origin: http://172.16.8.12
Referer: http://172.16.8.12/
DNT: 1
Sec-GPC: 1
臨時檔案/tmp/nginx/0000000001:
-----------------------------264635460442698726183359332565
Content-Disposition: form-data; name="file"; filename="paketti.ipk"
Content-Type: application/octet-stream
kissakissakissa
kissakissakissa
kissakissakissa
-----------------------------264635460442698726183359332565--
代碼:
class S(BaseHTTPRequestHandler):
def do_POST(self):
temp_filename = self.headers['X-FILE']
temp_file_pointer=open(temp_filename,"rb")
form = cgi.FieldStorage( fp=temp_file_pointer, headers=self.headers, environ={'REQUEST_METHOD':'POST', 'CONTENT_TYPE':self.headers['Content-Type'], 'CONTENT_LENGTH':self.headers['Content-Length'] }, )
actual_filename = form['file'].filename
logging.info("ACTUAL FILENAME={}".format(actual_filename))
open("/tmp/nginx/{}".format(actual_filename), "wb").write(form['file'].file.read())
logging.info("FORM={}".format(form))
現在最奇怪的事情。日志顯示:
INFO:root:ACTUAL FILENAME=paketti.ipk
INFO:root:FORM=FieldStorage(None, None, [FieldStorage('file', 'paketti.ipk', b'')])
查看 /tmp/nginx 目錄:
root@am335x-evm:/tmp# ls -la /tmp/nginx/*
-rw------- 1 www www 286 May 18 20:48 /tmp/nginx/0000000001
-rw-r--r-- 1 root root 0 May 18 20:48 /tmp/nginx/paketti.ipk
因此,這就像部分作業,因為名稱已獲得。但是為什么它不決議資料內容呢?我錯過了什么?
這在 python 上是否可行,或者我應該只撰寫一個 C 實用程式?該檔案為 70 MB,如果我在記憶體中讀取它,OOM-killer 會殺死 python3 行程(我會說這是理所當然的)。但是,是的,資料內容去哪里了?
uj5u.com熱心網友回復:
代替cgi模塊需要一個多部分決議器,它可以流式傳輸資料,而不是將所有資料讀取到 RAM。AFAIK 標準庫中沒有任何用處,但此模塊可能有用:https ://github.com/defnull/multipart
或者,按照這些思路DIY一些東西應該可以作業:
boundary = b"-----whatever"
# Begin and end lines (as per your example, I didn't check the RFCs)
begin = b"\r\n%b\r\n" % boundary
end = b"\r\n%b--\r\n" % boundary
# Prefer with blocks to open files so that they are also closed properly
with open(temp_filename, "rb") as f:
buf = bytearray()
# Search for the boundary
while begin not in buf:
block = f.read(4096)
if not block: raise ValueError("EOF without boundary begin")
buf = buf[-1024:] block # Keep up to 5 KiB buffered
# Delete buffer contents until the end of the boundary
del buf[:buf.find(begin) len(begin)]
# Copy data to another file (or do what you need to do with it)
with open("output.dat", "wb") as f2:
while end not in buf:
f2.write(buf[:-1024])
del buf[:-1024]
buf = f.read(4096)
if not buf: raise ValueError("EOF without boundary end")
f2.write(buf[:buf.find(end)])
假定邊界最多只有 1024 個位元組。您可以使用實際長度來代替完美。
uj5u.com熱心網友回復:
游戲中的問題比我最初想象的要多。
首先,/tmp 來自最大大小為 120MB 的 tmpfs。
其次,我的 nginx.conf 有問題。我需要注釋掉這樣的東西來清理它:
#client_body_in_file_only on
#proxy_set_header X-FILE $request_body_file;
#proxy_set_body $request_body_file;
然后我需要添加這些
proxy_redirect off; # Maybe not that importnat
proxy_request_buffering off; # Very important
在此之后的代碼
form = cgi.FieldStorage( fp=self.rfile, headers=self.headers, environ={'REQUEST_METHOD':'POST', 'CONTENT_TYPE':self.headers['Content-Type'], })
開始“作業”。我正在監視 /tmp 的使用情況,它首先使用 70MB,然后使用完整的 120MB。上傳的檔案被截斷為 50 MB。
因此,當我在 4096 個字符的回圈中讀取和寫入決議的 cgi.FieldStorage 時,系統會自動將其完全讀取到 /tmp 中的某個位置,然后嘗試寫入最終檔案并遇到“設備上沒有剩余空間”錯誤.
為了解決這個問題,我保留了 nginx.conf 的附加內容,并在回圈中手動讀取 self.rfile,完全讀取 ['Content-Length'] (其他任何東西都會讓它變得瘋狂)。這樣一來就可以將其保存干凈;/tmp 的使用不超過一次 70MB。
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/480561.html
標籤:Python nginx cgi python-3.5
