@@ -644,6 +644,9 @@ class OplogExport(RoleBasedAccessControlMixin, SingleObjectMixin, View):
644644 model = Oplog
645645 valid_include_values = {"recordings" , "evidence" , "all" }
646646 attachment_chunk_size = 1024 * 1024
647+ recoverable_attachment_errors = (OSError , RuntimeError , TypeError , ValueError )
648+ path_lookup_errors = (AttributeError , NotImplementedError , OSError , TypeError , ValueError )
649+ close_errors = (AttributeError , OSError , ValueError )
647650
648651 def test_func (self ):
649652 return self .get_object ().user_can_view (self .request .user )
@@ -659,7 +662,7 @@ def _write_file_to_zip(self, zf, field_file, arcname):
659662 if os .path .exists (path ):
660663 zf .write (path , arcname )
661664 return True
662- except Exception :
665+ except self . path_lookup_errors :
663666 pass
664667
665668 try :
@@ -671,7 +674,7 @@ def _write_file_to_zip(self, zf, field_file, arcname):
671674 finally :
672675 try :
673676 field_file .close ()
674- except Exception :
677+ except self . close_errors :
675678 pass
676679
677680 @staticmethod
@@ -736,6 +739,8 @@ def get(self, *args, **kwargs):
736739
737740 # Build the ZIP in a spooled temp file so small exports stay in memory and
738741 # larger ones spill to disk without buffering every attachment in RAM.
742+ # Do not wrap this in ``with``: FileResponse needs the file handle to remain
743+ # open after this method returns so Django can stream it to the client.
739744 tmp = tempfile .SpooledTemporaryFile (max_size = 50 * 1024 * 1024 , mode = "w+b" )
740745 manifest = {"generated_at" : datetime .utcnow ().isoformat () + "Z" , "entries" : {}}
741746 attachments_map = {}
@@ -754,7 +759,7 @@ def get(self, *args, **kwargs):
754759 arcname = self ._next_arcname ("recordings" , entry .id , fname , used_archive_names )
755760 try :
756761 self ._write_file_to_zip (zf , rec .recording_file , arcname )
757- except Exception :
762+ except self . recoverable_attachment_errors :
758763 logger .exception ("Could not include recording for entry %s" , entry .id )
759764 continue
760765 attachments_map [str (entry .id )]["recordings" ].append (arcname )
@@ -771,7 +776,7 @@ def get(self, *args, **kwargs):
771776 arcname = self ._next_arcname ("evidence" , entry .id , fname , used_archive_names )
772777 try :
773778 self ._write_file_to_zip (zf , ev .document , arcname )
774- except Exception :
779+ except self . recoverable_attachment_errors :
775780 logger .exception (
776781 "Could not include evidence %s for entry %s" ,
777782 getattr (ev , "pk" , "?" ),
0 commit comments