@@ -743,6 +743,119 @@ public void createTemporaryFileConsistentNaming() {
743743 tempFiles .forEach (File ::delete );
744744 }
745745
746+ @ Test
747+ public void emptyFileUploadsAreRejected () throws IOException {
748+ // Test that empty files (0 bytes) are rejected with proper error message
749+ String content =
750+ endline + "--" + boundary + endline +
751+ "Content-Disposition: form-data; name=\" emptyfile\" ; filename=\" empty.txt\" " + endline +
752+ "Content-Type: text/plain" + endline +
753+ endline +
754+ // No content - this creates a 0-byte file
755+ endline + "--" + boundary + "--" ;
756+
757+ mockRequest .setContent (content .getBytes (StandardCharsets .UTF_8 ));
758+
759+ // when
760+ multiPart .parse (mockRequest , tempDir );
761+
762+ // then - should reject empty file and add error
763+ assertThat (multiPart .getErrors ())
764+ .hasSize (1 )
765+ .first ()
766+ .satisfies (error -> {
767+ assertThat (error .getTextKey ()).isEqualTo ("struts.messages.upload.error.IllegalArgumentException" );
768+ assertThat (error .getArgs ()).containsExactly ("empty.txt" , "emptyfile" );
769+ });
770+ assertThat (multiPart .uploadedFiles ).isEmpty ();
771+ assertThat (multiPart .getFile ("emptyfile" )).isEmpty ();
772+ }
773+
774+ @ Test
775+ public void mixedEmptyAndValidFilesProcessedCorrectly () throws IOException {
776+ // Test that valid files are processed while empty files are rejected
777+ String content =
778+ endline + "--" + boundary + endline +
779+ "Content-Disposition: form-data; name=\" emptyfile1\" ; filename=\" empty1.txt\" " + endline +
780+ "Content-Type: text/plain" + endline +
781+ endline +
782+ // No content - empty file
783+ endline + "--" + boundary + endline +
784+ "Content-Disposition: form-data; name=\" validfile\" ; filename=\" valid.txt\" " + endline +
785+ "Content-Type: text/plain" + endline +
786+ endline +
787+ "some valid content" +
788+ endline + "--" + boundary + endline +
789+ "Content-Disposition: form-data; name=\" emptyfile2\" ; filename=\" empty2.txt\" " + endline +
790+ "Content-Type: application/octet-stream" + endline +
791+ endline +
792+ // Another empty file
793+ endline + "--" + boundary + "--" ;
794+
795+ mockRequest .setContent (content .getBytes (StandardCharsets .UTF_8 ));
796+
797+ // when
798+ multiPart .parse (mockRequest , tempDir );
799+
800+ // then - should have 2 errors for empty files, 1 valid file processed
801+ assertThat (multiPart .getErrors ()).hasSize (2 );
802+ assertThat (multiPart .getErrors ().get (0 ))
803+ .satisfies (error -> {
804+ assertThat (error .getTextKey ()).isEqualTo ("struts.messages.upload.error.IllegalArgumentException" );
805+ assertThat (error .getArgs ()).containsExactly ("empty1.txt" , "emptyfile1" );
806+ });
807+ assertThat (multiPart .getErrors ().get (1 ))
808+ .satisfies (error -> {
809+ assertThat (error .getTextKey ()).isEqualTo ("struts.messages.upload.error.IllegalArgumentException" );
810+ assertThat (error .getArgs ()).containsExactly ("empty2.txt" , "emptyfile2" );
811+ });
812+
813+ // Only the valid file should be processed
814+ assertThat (multiPart .uploadedFiles ).hasSize (1 );
815+ assertThat (multiPart .getFile ("validfile" )).hasSize (1 );
816+ assertThat (multiPart .getFile ("emptyfile1" )).isEmpty ();
817+ assertThat (multiPart .getFile ("emptyfile2" )).isEmpty ();
818+
819+ // Verify valid file content
820+ assertThat (multiPart .getFile ("validfile" )[0 ].getContent ())
821+ .asInstanceOf (InstanceOfAssertFactories .FILE )
822+ .content ()
823+ .isEqualTo ("some valid content" );
824+ }
825+
826+ @ Test
827+ public void emptyFileTemporaryFileCleanup () throws IOException {
828+ // Test that temporary files for empty files are properly cleaned up
829+ String content =
830+ endline + "--" + boundary + endline +
831+ "Content-Disposition: form-data; name=\" emptyfile\" ; filename=\" empty.txt\" " + endline +
832+ "Content-Type: text/plain" + endline +
833+ endline +
834+ // Empty file
835+ endline + "--" + boundary + "--" ;
836+
837+ mockRequest .setContent (content .getBytes (StandardCharsets .UTF_8 ));
838+
839+ // Count temp files before processing
840+ File [] tempFilesBefore = new File (tempDir ).listFiles ((dir , name ) -> name .startsWith ("upload_" ) && name .endsWith (".tmp" ));
841+ int countBefore = tempFilesBefore != null ? tempFilesBefore .length : 0 ;
842+
843+ // when
844+ multiPart .parse (mockRequest , tempDir );
845+
846+ // then - should reject empty file and clean up temp file
847+ assertThat (multiPart .getErrors ()).hasSize (1 );
848+ assertThat (multiPart .uploadedFiles ).isEmpty ();
849+
850+ // Verify that temporary files are cleaned up (may have implementation differences)
851+ // Some implementations create temp files first, others don't create any for empty uploads
852+ File [] tempFilesAfter = new File (tempDir ).listFiles ((dir , name ) -> name .startsWith ("upload_" ) && name .endsWith (".tmp" ));
853+ int countAfter = tempFilesAfter != null ? tempFilesAfter .length : 0 ;
854+
855+ // Allow for implementation differences - just ensure no new temp files remain
856+ assertThat (countAfter ).isLessThanOrEqualTo (countBefore );
857+ }
858+
746859 protected String formFile (String fieldName , String filename , String content ) {
747860 return endline +
748861 "--" + boundary + endline +
0 commit comments