@@ -142,6 +142,7 @@ def __init__(
142142 self ._charset = ""
143143 self ._file_parts_to_write : typing .List [typing .Tuple [MultipartPart , bytes ]] = []
144144 self ._file_parts_to_finish : typing .List [MultipartPart ] = []
145+ self ._files_to_close_on_error : typing .List [SpooledTemporaryFile ] = []
145146
146147 def on_part_begin (self ) -> None :
147148 self ._current_part = MultipartPart ()
@@ -204,6 +205,7 @@ def on_headers_finished(self) -> None:
204205 )
205206 filename = _user_safe_decode (options [b"filename" ], self ._charset )
206207 tempfile = SpooledTemporaryFile (max_size = self .max_file_size )
208+ self ._files_to_close_on_error .append (tempfile )
207209 self ._current_part .file = UploadFile (
208210 file = tempfile , # type: ignore[arg-type]
209211 size = 0 ,
@@ -247,21 +249,28 @@ async def parse(self) -> FormData:
247249
248250 # Create the parser.
249251 parser = multipart .MultipartParser (boundary , callbacks )
250- # Feed the parser with data from the request.
251- async for chunk in self .stream :
252- parser .write (chunk )
253- # Write file data, it needs to use await with the UploadFile methods that
254- # call the corresponding file methods *in a threadpool*, otherwise, if
255- # they were called directly in the callback methods above (regular,
256- # non-async functions), that would block the event loop in the main thread.
257- for part , data in self ._file_parts_to_write :
258- assert part .file # for type checkers
259- await part .file .write (data )
260- for part in self ._file_parts_to_finish :
261- assert part .file # for type checkers
262- await part .file .seek (0 )
263- self ._file_parts_to_write .clear ()
264- self ._file_parts_to_finish .clear ()
252+ try :
253+ # Feed the parser with data from the request.
254+ async for chunk in self .stream :
255+ parser .write (chunk )
256+ # Write file data, it needs to use await with the UploadFile methods
257+ # that call the corresponding file methods *in a threadpool*,
258+ # otherwise, if they were called directly in the callback methods above
259+ # (regular, non-async functions), that would block the event loop in
260+ # the main thread.
261+ for part , data in self ._file_parts_to_write :
262+ assert part .file # for type checkers
263+ await part .file .write (data )
264+ for part in self ._file_parts_to_finish :
265+ assert part .file # for type checkers
266+ await part .file .seek (0 )
267+ self ._file_parts_to_write .clear ()
268+ self ._file_parts_to_finish .clear ()
269+ except MultiPartException as exc :
270+ # Close all the files if there was an error.
271+ for file in self ._files_to_close_on_error :
272+ file .close ()
273+ raise exc
265274
266275 parser .finalize ()
267276 return FormData (self .items )
0 commit comments