@@ -23,6 +23,7 @@ void SDFileServer::dump_config() {
23
23
ESP_LOGCONFIG (TAG, " Deletation Enabled: %s" , TRUEFALSE (this ->deletion_enabled_ ));
24
24
ESP_LOGCONFIG (TAG, " Download Enabled : %s" , TRUEFALSE (this ->download_enabled_ ));
25
25
ESP_LOGCONFIG (TAG, " Upload Enabled : %s" , TRUEFALSE (this ->upload_enabled_ ));
26
+ ESP_LOGCONFIG (TAG, " Buf size : %d" , this ->buf_size_ );
26
27
}
27
28
28
29
bool SDFileServer::canHandle (AsyncWebServerRequest *request) {
@@ -91,7 +92,9 @@ void SDFileServer::set_download_enabled(bool allow) { this->download_enabled_ =
91
92
92
93
void SDFileServer::set_upload_enabled (bool allow) { this ->upload_enabled_ = allow; }
93
94
94
- void SDFileServer::handle_get (AsyncWebServerRequest *request) const {
95
+ void SDFileServer::set_buf_size (size_t buf_size) { this ->buf_size_ = buf_size; }
96
+
97
+ void SDFileServer::handle_get (AsyncWebServerRequest *request) {
95
98
std::string extracted = this ->extract_path_from_url (std::string (request->url ().c_str ()));
96
99
std::string path = this ->build_absolute_path (extracted);
97
100
@@ -318,66 +321,88 @@ void SDFileServer::handle_index(AsyncWebServerRequest *request, std::string cons
318
321
319
322
request->send (response);
320
323
}
321
- void SDFileServer::handle_download (AsyncWebServerRequest *request, std::string const &path) const {
324
+ void SDFileServer::handle_download (AsyncWebServerRequest *request, std::string const &path) {
322
325
if (!this ->download_enabled_ ) {
323
326
request->send (401 , " application/json" , " { \" error\" : \" file download is disabled\" }" );
324
327
return ;
325
328
}
326
329
size_t read_len = 0 ;
327
330
328
331
#ifdef USE_ESP_IDF
329
- size_t file_size = this ->sd_mmc_card_ ->file_size (path);
330
- if (file_size <= 0 ) {
331
- ESP_LOGE (TAG, " File not found: %s" ,path.c_str ());
332
- request->send (401 , " application/json" , " { \" error\" : \" file not found or empty.\" }" );
333
- return ;
334
- }
335
-
336
- RAMAllocator<uint8_t > allocator (ExternalRAMAllocator<uint8_t >::ALLOW_FAILURE);
337
- uint8_t *data = allocator.allocate (file_size);
338
-
339
- if (data == nullptr ) {
340
- ESP_LOGE (TAG, " No memory. (Size: %zu)" , file_size);
341
- request->send (401 , " application/json" , " { \" error\" : \" no memory.\" }" );
342
- return ;
343
- }
344
-
345
- read_len = this ->sd_mmc_card_ ->read_file (path,data,file_size);
346
-
347
- if (read_len == 0 ) {
348
- ESP_LOGE (TAG, " Failed to read file: %s" ,path.c_str ());
349
- allocator.deallocate (data,file_size);
350
- request->send (401 , " application/json" , " { \" error\" : \" failed to read file\" }" );
351
- return ;
352
- }
353
-
354
- auto *response = request->beginResponse_P (200 , Path::mime_type (path).c_str (), data, read_len);
355
-
356
- #else
332
+ httpd_req_t *req = *request;
333
+ httpd_resp_set_status (req, HTTPD_200);
334
+ auto mt = new std::string (Path::mime_type (path));
335
+ httpd_resp_set_type (req, mt->c_str ());
336
+ std::string fname = std::string (" inline; filename=" ) + Path::file_name (path);
337
+ httpd_resp_set_hdr (req, " Content-Disposition" , fname.c_str ());
338
+ std::string content_len = std::to_string (this ->sd_mmc_card_ ->file_size (path));
339
+ httpd_resp_set_hdr (req, " content-length" , content_len.c_str ());
340
+ httpd_resp_set_hdr (req, " access-control-allow-origin" , " *" );
341
+ httpd_resp_set_hdr (req, " Cache-Control" , " no-cache" );
342
+ #endif
343
+ sd_mmc_card::FilePtr *fptr = this ->sd_mmc_card_ ->open_file (path.c_str (), " r" );
344
+ if (fptr == nullptr )
345
+ {
346
+ ESP_LOGE (TAG, " Failed to open file: %s" , path.c_str ());
347
+ request->send (401 , " application/json" , " { \" error\" : \" failed to open file\" }" );
348
+ return ;
349
+ }
350
+ #ifdef USE_ESP32_FRAMEWORK_ARDUINO
351
+ request->onDisconnect ({
352
+ this ->sd_mmc_card_ ->close_file (fptr);
353
+ });
354
+ auto response = request->beginResponse (Path::mime_type (path), this ->sd_mmc_card_ ->file_size (path),
355
+ [](uint8_t *buffer, size_t maxLen, size_t index ) -> size_t
356
+ {
357
+ read_len = this ->sd_mmc_card_ ->block_read_file (fptr, buffer, maxLen);
358
+ if (read_len < 0 )
359
+ {
360
+ ESP_LOGE (TAG, " Error sending file data" );
361
+ read_len = 0 ;
362
+ }
363
+ else
364
+ ESP_LOGV (TAG, " Read file block %d" , read_len);
365
+ return read_len;
366
+ });
367
+ std::string fname = std::string (" attachment; filename=" ) + Path::file_name (path);
368
+ response->addHeader (" access-control-allow-origin" ," *" );
369
+ response->addHeader (" Content-Disposition" ,fname.c_str ());
370
+ request->send (response);
371
+ #endif
357
372
358
- auto file = this ->sd_mmc_card_ ->read_file (path);
373
+ #ifdef USE_ESP_IDF
374
+ uint8_t *buff = this ->allocator_ .allocate (this ->buf_size_ );
359
375
360
- if (file.size () == 0 ) {
361
- request->send (401 , " application/json" , " { \" error\" : \" failed to read file\" }" );
376
+ if (buff == nullptr ) {
377
+ ESP_LOGE (TAG, " No memory: %d" , this ->buf_size_ );
378
+ request->send (401 , " application/json" , " { \" error\" : \" no memory\" }" );
362
379
return ;
363
380
}
364
- auto *response = request->beginResponseStream (Path::mime_type (path).c_str (), file.size ());
365
- response->write (file.data (), file.size ());
366
- read_len = file.size ();
367
- #endif
368
-
369
- ESP_LOGD (TAG, " Send file: %s, size %d" , path.c_str (), read_len);
370
- request->send (response);
381
+ do {
382
+ read_len = this ->sd_mmc_card_ ->block_read_file (fptr, buff, this ->buf_size_ );
383
+ ESP_LOGV (TAG, " Read file block %d" , read_len);
384
+ if (read_len >= 0 )
385
+ {
386
+ esp_err_t ret = httpd_resp_send_chunk (req, (const char *)buff, read_len);
387
+ if (ret != ESP_OK)
388
+ {
389
+ this ->sd_mmc_card_ ->close_file (fptr);
390
+ ESP_LOGE (TAG, " Error send file data: %s" , esp_err_to_name (ret));
391
+ this ->allocator_ .deallocate (buff, this ->buf_size_ );
392
+ // request->send(500, "application/json", "{ \"error\": \"no memory\" }");
393
+ return ;
394
+ }
395
+ else
396
+ ESP_LOGV (TAG, " Send file chunk %d" , read_len);
397
+ }
398
+ } while (read_len > 0 );
399
+ ESP_LOGD (TAG, " Send complete" );
371
400
372
- #ifdef USE_ESP_IDF
373
- // TODO: esp_http_server_dispatch_event(HTTP_SERVER_EVENT_SENT_DATA, &evt_data, sizeof(esp_http_server_event_data));
374
- // return ESP_OK;
375
- allocator.deallocate (data,file_size);
401
+ this ->sd_mmc_card_ ->close_file (fptr);
402
+ this ->allocator_ .deallocate (buff, this ->buf_size_ );
376
403
#endif
377
404
}
378
405
379
-
380
-
381
406
void SDFileServer::handle_delete (AsyncWebServerRequest *request) {
382
407
if (!this ->deletion_enabled_ ) {
383
408
request->send (401 , " application/json" , " { \" error\" : \" file deletion is disabled\" }" );
@@ -494,7 +519,8 @@ std::string Path::mime_type(std::string const &file) {
494
519
{" csv" , " text/csv" }, {" html" , " text/html" }, {" css" , " text/css" }, {" js" , " text/javascript" },
495
520
{" json" , " application/json" }, {" xml" , " application/xml" }, {" zip" , " application/zip" }, {" gz" , " application/gzip" },
496
521
{" tar" , " application/x-tar" }, {" mp4" , " video/mp4" }, {" avi" , " video/x-msvideo" }, {" webm" , " video/webm" }};
497
-
522
+
523
+ // auto oct_stream = std::string("application/octet-stream");
498
524
std::string ext = Path::extension (file);
499
525
ESP_LOGD (TAG, " ext : %s" , ext.c_str ());
500
526
if (!ext.empty ()) {
@@ -503,7 +529,7 @@ std::string Path::mime_type(std::string const &file) {
503
529
if (it != file_types.end ())
504
530
return it->second ;
505
531
}
506
- return " application/octet-stream" ;
532
+ return std::string ( " application/octet-stream" ) ;
507
533
}
508
534
509
535
} // namespace sd_file_server
0 commit comments