ESP32 file upload via POST request to REST API

71 Views Asked by At

TL;DR - hoping for that rubber duck debug moment, I posted a minimalist example of the problem in action, if you just want to view it here. The problem file on that branch is here (...around line 270). Of course, there's a README in each sub directory to help get the full system running locally.

Objective

Upload an audio sample from a connected device. The upload is coming from an ESP32 device. Specifically, this example targets the Seeed Studio XIAO ESP32 S3 (Sense).

For all intents and purposes, I suppose this same solution could be applied to any type of file, not just audio (... I guess I should've just tried a txt file first).

The System

The overall system is comprised of the ESP32 device, a REST API built on NestJS and MongoDB as the datastore.

For testing and management purposes, I also built a web-based UI. Feel free to check the example repo if you want to run the whole, entire thing locally.

I have tested the system with audio files uploaded through the web UI. I have also tested the system with audio recorded through the web interface as well.

Both of those approaches work just fine -- I know there isn't a problem with those pieces of the solution.

FWIW - I'm using VS Code and the PlatformIO Extension.

The Problem Area

I have tried several different ways of writing the audio to an HttpClient stream.

If anyone cares to look, I created a branch for each variant of my attempts. The latest being dev-8

I have gotten the device to make a POST request and see a record in the MongoDB collection, but ALL the audio data is missing from the file.

Here is my latest effort -- apologies for the long-winded code snippet -- can someone please help identify the problem?:

void uploadFile() {
    try {
        if (sessionId == 0) sessionId = millis();
        Serial.print("Spewing chunks now, Session ID: ");
        Serial.println(sessionId);

        char head[128];
        sprintf(head, "--%s\r\nContent-Disposition: form-data; name=\"file\"; filename=\"%s\"\r\nContent-Type: audio/wav\r\n\r\n", AUDIO_FILE_BOUNDARY, WAV_FILE_NAME);

        char tail[64];
        sprintf(tail, "\r\n--%s--\r\n", AUDIO_FILE_BOUNDARY);

        Serial.println("\t==> Begin Request.");

        client.beginRequest();
        client.post(API_ENDPOINT_MFILE);
        client.sendHeader("Connection", "keep-alive");
        client.sendHeader("Accept", "*/*");
        client.sendHeader("Audio-Source", "streaming-from-device");
        client.sendHeader("SessionID", sessionId);

        // Using the device MAC address as its unique identifier.
        String s(ESP.getEfuseMac());
        client.sendHeader("Device-ID", s);

        File file = SD.open("/" WAV_FILE_NAME, FILE_READ);
        const size_t fLen = file.size();

        client.sendHeader("Content-Length", String(fLen));
        client.sendHeader("Content-Type", "multipart/form-data; boundary=" AUDIO_FILE_BOUNDARY);

        char* fileBuffer = new char[fLen + 1];
        Serial.printf("\t==> File Len:  %d\n", fLen);
        if (file.available()) {
            file.readBytesUntil('\0', fileBuffer, fLen);
            fileBuffer[fLen + 1] = '\0';
        }
        file.close();
        client.beginBody();
        client.println(head);

        /**
         * @brief Here is the problem
         * I even used Github Copilot to help me write this code.
         */
        try {
            int offSet = 0;
            while (offSet < fLen) {
                int chunkSize = 1024;
                Serial.printf("\t==> offSet: %d\n", offSet);
                if (offSet + chunkSize > fLen) {
                    chunkSize = fLen - offSet;
                }
                client.write((uint8_t*)(fileBuffer + offSet), chunkSize);
                offSet += chunkSize;
            }

            client.write('\0');
        } catch (...) {
            Serial.println("\n==== ERROR ===");
            Serial.println("Could not write the file to the stream.\n");
        }

        client.println(tail);
        client.flush();
        client.endRequest();
        Serial.println("\n============> DONE WRITING.\n");
        while (!client.available())
            ;
        readResponse();
    } catch (...) {
        Serial.println("\n\tThere was a problem transmitting audio.\n");
        _resetSession();
    }
}
0

There are 0 best solutions below