-->
Page 1 of 1

Async WebServer upload with directory

PostPosted: Sun Sep 10, 2017 8:14 am
by treii28
I'm trying to implement an upload function but the example with the Async server and the Async server with SD both seem to plop uploads into the equivalent of the root directory when using SPIFFS and a flash file system. I'm trying to figure out how the parameters work and either must be doing something wrong or don't understand what is going on.

Using postman sending form-data in a POST with a file and a param key 'dir' I'm sending a file that does seem to trigger an upload, but no matter what I try, I can't seem to get it to recognize the 'dir' parameter.

From the server init:
Code: Select all    server.on("/upload", HTTP_POST, returnOK, handleUpload);


handleUpload method:
Code: Select allvoid handleUpload(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final){
    struct uploadRequest {
        uploadRequest* next;
        AsyncWebServerRequest *request;
        File uploadFile;
        uint32_t fileSize;
        uploadRequest(){next = NULL; request = NULL; fileSize = 0;}
    };
    static uploadRequest uploadRequestHead;
    uploadRequest* thisUploadRequest = NULL;

    if( ! index){
        String toFile = filename;
        if(request->hasParam("dir", true)) {
            AsyncWebParameter* p = request->getParam("dir", true);
            DBG_OUTPUT_PORT.println("dir param: " + p->value());
            toFile = p->value();
            if(!toFile.endsWith("/"))
                toFile += "/";
            toFile += filename;
        }
        if(!toFile.startsWith("/"))
            toFile = "/" + toFile ;

        if(SPIFFS.exists(toFile))
            SPIFFS.remove(toFile);
        thisUploadRequest = new uploadRequest;
        thisUploadRequest->request = request;
        thisUploadRequest->next = uploadRequestHead.next;
        uploadRequestHead.next = thisUploadRequest;
        thisUploadRequest->uploadFile = SPIFFS.open(toFile, "w");
        DBG_OUTPUT_PORT.println("Upload: START, filename: " + toFile);
    }
    else{
        thisUploadRequest = uploadRequestHead.next;
        while(thisUploadRequest->request != request) thisUploadRequest = thisUploadRequest->next;
    }

    if(thisUploadRequest->uploadFile){
        for(size_t i=0; i<len; i++){
            thisUploadRequest->uploadFile.write(data[i]);
        }
        thisUploadRequest->fileSize += len;
    }

    if(final){
        thisUploadRequest->uploadFile.close();
        DBG_OUTPUT_PORT.print("Upload: END, Size: "); DBG_OUTPUT_PORT.println(thisUploadRequest->fileSize);
        uploadRequest* linkUploadRequest = &uploadRequestHead;
        while(linkUploadRequest->next != thisUploadRequest) linkUploadRequest = linkUploadRequest->next;
        linkUploadRequest->next = thisUploadRequest->next;
        delete thisUploadRequest;
    }
}


The conditional if(request->hasParam("dir", true)) doesn't fire whether I send dir in the POST variables or even if I add it to the url as a GET parameter.

Re: Async WebServer upload with directory

PostPosted: Sun Sep 10, 2017 8:57 am
by treii28
OK, with some poking and prodding I found out that it will take a GET parameter, thus I just have to remove the 'true' from the hasParam and getParam and include the dir as a GET key/value on the url.

Re: Async WebServer upload with directory

PostPosted: Mon Sep 11, 2017 10:43 am
by tele_player
Does SPIFFS on 8266 have directories?

No, but it allows '/' in filenames, so a path can look like it has directories.

Re: Async WebServer upload with directory

PostPosted: Tue Sep 12, 2017 8:05 am
by treii28
tele_player wrote:Does SPIFFS on 8266 have directories?

No, but it allows '/' in filenames, so a path can look like it has directories.


Yes, I am aware of that, but it includes an 'openDir' method with a .next() sub method that will at least "try" to emulate a directory structure based on a partial file prefix. (and believe me, it was a bit of fun trying to figure out how to emulate behavior similar to 'isDirectory" or "directoryExists" type behavior using this fudged emulation).

I eventually figured out the trick to trying to determine if something is a path prefix emulating a directory is to take whatever the url path is, check if it ends with '/' and if it doesn't, add a '/' to the end of it and try to 'openDir' on it. Obviously, if a path points to something like "/index.htm", unless a real idiot set up the filesystem (which I am not), there shouldn't be a file called "/index.htm/". By code enforcing the creation of files and checking the uploading of files, I can prevent such things from being created.

It's about as fudgy as the emulation of paths by adding the "/" characters to the filenames, but I'm starting to get a tad more functionality and weed out the potential bug-a-boos in doing it. Then the biggest remaining limitation is the 32 character filename maximum.

I'm finding there's more than enough room on the typical ESP-12* to handle more web content than I will ever need on one of these devices and while I'm restricted to 32 characters for paths (including the "/" characters), the 8x3 restriction of using SD plus the need to tack on additional hardware connections makes using the SPIFFS a much more preferable solution.