@@ -670,38 +670,52 @@ void AsyncFileResponse::_setContentTypeFromPath(const String &path) {
670
670
#endif
671
671
}
672
672
673
+ /* *
674
+ * @brief Constructor for AsyncFileResponse that handles file serving with compression support
675
+ *
676
+ * This constructor creates an AsyncFileResponse object that can serve files from a filesystem,
677
+ * with automatic fallback to gzip-compressed versions if the original file is not found.
678
+ * It also handles ETag generation for caching and supports both inline and download modes.
679
+ *
680
+ * @param fs Reference to the filesystem object used to open files
681
+ * @param path Path to the file to be served (without compression extension)
682
+ * @param contentType MIME type of the file content (empty string for auto-detection)
683
+ * @param download If true, file will be served as download attachment; if false, as inline content
684
+ * @param callback Template processor callback for dynamic content processing
685
+ */
673
686
AsyncFileResponse::AsyncFileResponse (FS &fs, const String &path, const char *contentType, bool download, AwsTemplateProcessor callback)
674
687
: AsyncAbstractResponse(callback) {
675
- _code = 200 ;
676
- const String gzPath = path + asyncsrv::T__gz;
677
-
678
- if (!download && !fs.exists (path) && fs.exists (gzPath)) {
679
- _path = gzPath;
680
- _content = fs.open (gzPath, fs::FileOpenMode::read);
688
+ // Try to open the uncompressed version first
689
+ _content = fs.open (path, fs::FileOpenMode::read);
690
+ if (_content.available ()) {
691
+ _path = path;
692
+ _contentLength = _content.size ();
693
+ } else {
694
+ // Try to open the compressed version (.gz)
695
+ _path = path + asyncsrv::T__gz;
696
+ _content = fs.open (_path, fs::FileOpenMode::read);
681
697
_contentLength = _content.size ();
682
- addHeader (T_Content_Encoding, T_gzip, false );
683
- _callback = nullptr ; // Unable to process zipped templates
684
- _sendContentLength = true ;
685
- _chunked = false ;
686
698
687
- // CRC32-based ETag of the trailer, bytes 4-7 from the end
688
- _content.seek (_contentLength - 8 );
689
- uint8_t crcInTrailer[4 ];
690
- if (_content.read (crcInTrailer, sizeof (crcInTrailer)) == sizeof (crcInTrailer)) {
699
+ if (_content.seek (_contentLength - 8 )) {
700
+ addHeader (T_Content_Encoding, T_gzip, false );
701
+ _callback = nullptr ; // Unable to process zipped templates
702
+ _sendContentLength = true ;
703
+ _chunked = false ;
704
+
705
+ // Add ETag and cache headers
706
+ uint8_t crcInTrailer[4 ];
707
+ _content.read (crcInTrailer, sizeof (crcInTrailer));
691
708
char serverETag[9 ];
692
709
AsyncWebServerRequest::_getEtag (crcInTrailer, serverETag);
693
- addHeader (T_ETag, serverETag, false );
694
- addHeader (T_Cache_Control, T_no_cache, false );
695
- }
710
+ addHeader (T_ETag, serverETag, true );
711
+ addHeader (T_Cache_Control, T_no_cache, true );
696
712
697
- // Return to the beginning of the file
698
- _content.seek (0 );
699
- }
700
-
701
- if (!_content) {
702
- _path = path;
703
- _content = fs.open (path, fs::FileOpenMode::read);
704
- _contentLength = _content.size ();
713
+ _content.seek (0 );
714
+ } else {
715
+ // File is corrupted or invalid
716
+ _code = 404 ;
717
+ return ;
718
+ }
705
719
}
706
720
707
721
if (*contentType != ' \0 ' ) {
@@ -710,18 +724,19 @@ AsyncFileResponse::AsyncFileResponse(FS &fs, const String &path, const char *con
710
724
_contentType = contentType;
711
725
}
712
726
713
- int filenameStart = path.lastIndexOf (' /' ) + 1 ;
714
- char buf[26 + path.length () - filenameStart];
715
- char *filename = (char *)path.c_str () + filenameStart;
716
-
717
727
if (download) {
718
- // set filename and force download
728
+ // Extract filename from path and set as download attachment
729
+ int filenameStart = path.lastIndexOf (' /' ) + 1 ;
730
+ char buf[26 + path.length () - filenameStart];
731
+ char *filename = (char *)path.c_str () + filenameStart;
719
732
snprintf_P (buf, sizeof (buf), PSTR (" attachment; filename=\" %s\" " ), filename);
733
+ addHeader (T_Content_Disposition, buf, false );
720
734
} else {
721
- // set filename and force rendering
722
- snprintf_P (buf, sizeof (buf), PSTR (" inline" ));
735
+ // Serve file inline (display in browser)
736
+ addHeader (T_Content_Disposition, PSTR (" inline" ), false );
723
737
}
724
- addHeader (T_Content_Disposition, buf, false );
738
+
739
+ _code = 200 ;
725
740
}
726
741
727
742
AsyncFileResponse::AsyncFileResponse (File content, const String &path, const char *contentType, bool download, AwsTemplateProcessor callback)
0 commit comments