Use multiple upload instead of espfs image

pull/196/head
cskarai 8 years ago committed by Thorsten von Eicken
parent db8bba0990
commit 983fd549c1
  1. 4
      WEB-SERVER.md
  2. 114
      createEspFs.pl
  3. 213
      esp-link/cgiwebserversetup.c
  4. 2
      html/web-server.html
  5. 8
      httpd/multipart.c
  6. 8
      httpd/multipart.h

@ -38,7 +38,7 @@ Installation steps:
- 1: open EspLinkWebApp sample from Arduino - 1: open EspLinkWebApp sample from Arduino
- 2: upload the code onto an Arduino Nano/Uno - 2: upload the code onto an Arduino Nano/Uno
- 3: jump to the Web Server page on esp-link UI - 3: jump to the Web Server page on esp-link UI
- 4: upload web-page.espfs.img ( arduino/libraries/EspLink/examples/EspLinkWebApp/web-page.espfs.img ) - 4: upload the 3 HTML files in the samples ( multiple select from arduino/libraries/EspLink/examples/EspLinkWebApp/ )
- 5: jump to LED/User/Voltage pages - 5: jump to LED/User/Voltage pages
- 6: try out different settings - 6: try out different settings

@ -1,114 +0,0 @@
#!/usr/bin/perl
use strict;
use Data::Dumper;
my $dir = shift @ARGV;
my $out = shift @ARGV;
my $espfs = '';
my @structured = read_dir_structure($dir, "");
for my $file (@structured)
{
my $flags = 0;
my $name = $file;
my $compression = 0;
if( $name =~ /\.gz$/ )
{
$flags |= 2;
$name =~ s/\.gz$//;
}
my $head = '<!doctype html><html><head><title>esp-link</title><link rel=stylesheet href="/pure.css"><link rel=stylesheet href="/style.css"><meta name=viewport content="width=device-width, initial-scale=1"><script src="/ui.js"></script><script src="/userpage.js"></script></head><body><div id=layout>';
open IF, "<", "$dir/$file" or die "Can't read file: $!";
my @fc = <IF>;
close(IF);
my $cnt = join("", @fc);
if( $name =~ /\.html$/ )
{
if( ! ( $flags & 2 ) )
{
$cnt = "$head$cnt";
}
else
{
printf("TODO: prepend headers to GZipped HTML content!\n");
}
}
$name .= chr(0);
$name .= chr(0) while( (length($name) & 3) != 0 );
my $size = length($cnt);
$espfs .= "ESfs";
$espfs .= chr($flags);
$espfs .= chr($compression);
$espfs .= chr( length($name) & 255 );
$espfs .= chr( length($name) / 256 );
$espfs .= chr( $size & 255 );
$espfs .= chr( ( $size / 0x100 ) & 255 );
$espfs .= chr( ( $size / 0x10000 ) & 255 );
$espfs .= chr( ( $size / 0x1000000 ) & 255 );
$espfs .= chr( $size & 255 );
$espfs .= chr( ( $size / 0x100 ) & 255 );
$espfs .= chr( ( $size / 0x10000 ) & 255 );
$espfs .= chr( ( $size / 0x1000000 ) & 255 );
$espfs .= $name;
$cnt .= chr(0) while( (length($cnt) & 3) != 0 );
$espfs .= $cnt;
}
$espfs .= "ESfs";
$espfs .= chr(1);
for(my $i=0; $i < 11; $i++)
{
$espfs .= chr(0);
}
open FH, ">", $out or die "Can't open file for write, $!";
print FH $espfs;
close(FH);
exit(0);
sub read_dir_structure
{
my ($dir, $base) = @_;
my @files;
opendir my $dh, $dir or die "Could not open '$dir' for reading: $!\n";
while (my $file = readdir $dh) {
if ($file eq '.' or $file eq '..') {
next;
}
my $path = "$dir/$file";
if( -d "$path" )
{
my @sd = read_dir_structure($path, "$base/$file");
push @files, @sd ;
}
else
{
push @files, "$base/$file";
}
}
close( $dh );
$_ =~ s/^\/// for(@files);
return @files;
}

@ -9,8 +9,9 @@
#include "config.h" #include "config.h"
#include "web-server.h" #include "web-server.h"
int upload_offset = 0; // flash offset where to store page upload int header_position = 0; // flash offset of the file header
int html_header_len = 0; // HTML header length (for uploading HTML files) int upload_position = 0; // flash offset where to store page upload
int html_header_len = 0; // length of the HTML header added to the file
// this is the header to add if user uploads HTML file // this is the header to add if user uploads HTML file
const char * HTML_HEADER = "<!doctype html><html><head><title>esp-link</title>" const char * HTML_HEADER = "<!doctype html><html><head><title>esp-link</title>"
@ -18,127 +19,157 @@ const char * HTML_HEADER = "<!doctype html><html><head><title>esp-link</title>
"<meta name=viewport content=\"width=device-width, initial-scale=1\"><script src=\"/ui.js\">" "<meta name=viewport content=\"width=device-width, initial-scale=1\"><script src=\"/ui.js\">"
"</script><script src=\"/userpage.js\"></script></head><body><div id=layout> "; "</script><script src=\"/userpage.js\"></script></head><body><div id=layout> ";
// this method is for flash writing and erasing the page
// write is incremental, so whenever a page border is reached, the next page will be erased
int ICACHE_FLASH_ATTR webServerSetupWriteFlash( int addr, void * data, int length )
{
int end_addr = addr + length;
if( end_addr >= getUserPageSectionEnd() )
{
os_printf("No more space in the flash!\n");
return 1;
}
void * free_ptr = 0;
if(( length & 3 ) != 0 ) // ESP8266 always writes 4 bytes, so the remaining ones should be oxFF-ed out
{
free_ptr = os_malloc(length + 4);
os_memset(free_ptr, 0xFF, length + 4);
os_memcpy(free_ptr, data, length);
data = free_ptr;
}
int ptr = 0;
while( addr < end_addr )
{
if (addr % SPI_FLASH_SEC_SIZE == 0){
spi_flash_erase_sector(addr/SPI_FLASH_SEC_SIZE);
}
int max = (addr | (SPI_FLASH_SEC_SIZE - 1)) + 1;
int len = end_addr - addr;
if( end_addr > max )
len = max - addr;
spi_flash_write( addr, (uint32_t *)((char *)data + ptr), len );
ptr += len;
addr += len;
}
if( free_ptr != 0 )
os_free(free_ptr);
return 0;
}
// debug code
void ICACHE_FLASH_ATTR dumpFlash( int end )
{
int dump = getUserPageSectionStart();
while( dump < end )
{
char buffer[16];
spi_flash_read(dump, (uint32_t *)buffer, sizeof(buffer));
char dmpstr[sizeof(buffer)*3];
os_sprintf(dmpstr, "%06X: ", dump);
for(int i=0; i < sizeof(buffer); i++ )
os_sprintf(dmpstr + os_strlen(dmpstr), "%02X ", buffer[i]);
os_printf("%s\n", dmpstr);
dump += sizeof(buffer);
}
}
// multipart callback for uploading user defined pages // multipart callback for uploading user defined pages
int ICACHE_FLASH_ATTR webServerSetupMultipartCallback(MultipartCmd cmd, char *data, int dataLen, int position) int ICACHE_FLASH_ATTR webServerSetupMultipartCallback(MultipartCmd cmd, char *data, int dataLen, int position)
{ {
switch(cmd) switch(cmd)
{ {
case FILE_UPLOAD_START:
upload_position = getUserPageSectionStart();
header_position = upload_position;
break;
case FILE_START: case FILE_START:
upload_offset = 0;
html_header_len = 0;
// simple HTML file
if( ( dataLen > 5 ) && ( os_strcmp(data + dataLen - 5, ".html") == 0 ) ) // if the file ends with .html, wrap into an espfs image
{ {
// write the start block on esp-fs html_header_len = 0;
int spi_flash_addr = getUserPageSectionStart();
spi_flash_erase_sector(spi_flash_addr/SPI_FLASH_SEC_SIZE); // write the starting block on esp-fs
EspFsHeader hdr; EspFsHeader hdr;
hdr.magic = 0xFFFFFFFF; // espfs magic is invalid during upload hdr.magic = 0xFFFFFFFF; // espfs magic is invalid during upload
hdr.flags = 0; hdr.flags = 0;
hdr.compression = 0; hdr.compression = 0;
int len = dataLen + 1; int len = dataLen + 1;
while(( len & 3 ) != 0 ) while(( len & 3 ) != 0 )
len++; len++;
hdr.nameLen = len; hdr.nameLen = len;
hdr.fileLenComp = hdr.fileLenDecomp = 0xFFFFFFFF; hdr.fileLenComp = hdr.fileLenDecomp = 0xFFFFFFFF;
spi_flash_write( spi_flash_addr + upload_offset, (uint32_t *)(&hdr), sizeof(EspFsHeader) ); header_position = upload_position;
upload_offset += sizeof(EspFsHeader); if( webServerSetupWriteFlash( upload_position, (uint32_t *)(&hdr), sizeof(EspFsHeader) ) )
return 1;
upload_position += sizeof(EspFsHeader);
char nameBuf[len]; char nameBuf[len];
os_memset(nameBuf, 0, len); os_memset(nameBuf, 0, len);
os_memcpy(nameBuf, data, dataLen); os_memcpy(nameBuf, data, dataLen);
spi_flash_write( spi_flash_addr + upload_offset, (uint32_t *)(nameBuf), len ); if( webServerSetupWriteFlash( upload_position, (uint32_t *)(nameBuf), len ) )
upload_offset += len; return 1;
upload_position += len;
html_header_len = os_strlen(HTML_HEADER) & ~3; // upload only 4 byte aligned part
char buf[html_header_len]; // add header to HTML files
os_memcpy(buf, HTML_HEADER, html_header_len); if( ( dataLen > 5 ) && ( os_strcmp(data + dataLen - 5, ".html") == 0 ) ) // if the file ends with .html, wrap into an espfs image
spi_flash_write( spi_flash_addr + upload_offset, (uint32_t *)(buf), html_header_len );
upload_offset += html_header_len;
}
break;
case FILE_DATA:
if(( position < 4 ) && (upload_offset == 0)) // for espfs images check the magic number
{
for(int p = position; p < 4; p++ )
{ {
if( data[p - position] != ((ESPFS_MAGIC >> (p * 8) ) & 255 ) ) html_header_len = os_strlen(HTML_HEADER) & ~3; // upload only 4 byte aligned part
{ char buf[html_header_len];
os_printf("Not an espfs image!\n"); os_memcpy(buf, HTML_HEADER, html_header_len);
if( webServerSetupWriteFlash( upload_position, (uint32_t *)(buf), html_header_len ) )
return 1; return 1;
} upload_position += html_header_len;
data[p - position] = 0xFF; // espfs magic is invalid during upload
} }
} }
break;
int spi_flash_addr = getUserPageSectionStart() + upload_offset + position; case FILE_DATA:
int spi_flash_end_addr = spi_flash_addr + dataLen; if( webServerSetupWriteFlash( upload_position, data, dataLen ) )
if( spi_flash_end_addr + dataLen >= getUserPageSectionEnd() )
{
os_printf("No more space in the flash!\n");
return 1; return 1;
} upload_position += dataLen;
break;
int ptr = 0; case FILE_DONE:
while( spi_flash_addr < spi_flash_end_addr )
{ {
if (spi_flash_addr % SPI_FLASH_SEC_SIZE == 0){ // write padding after the file
spi_flash_erase_sector(spi_flash_addr/SPI_FLASH_SEC_SIZE); uint8_t pad_cnt = (4 - position) & 3;
if( pad_cnt ) {
uint32_t pad = 0;
if( webServerSetupWriteFlash( upload_position, &pad, pad_cnt ) )
return 1;
upload_position += pad_cnt;
} }
int max = (spi_flash_addr | (SPI_FLASH_SEC_SIZE - 1)) + 1; EspFsHeader hdr;
int len = spi_flash_end_addr - spi_flash_addr; hdr.magic = ESPFS_MAGIC;
if( spi_flash_end_addr > max ) hdr.fileLenComp = hdr.fileLenDecomp = position + html_header_len;
len = max - spi_flash_addr;
// restore ESPFS magic
spi_flash_write( spi_flash_addr, (uint32_t *)(data + ptr), len ); spi_flash_write( header_position + ((char *)&hdr.magic - (char*)&hdr), (uint32_t *)&hdr.magic, sizeof(uint32_t) );
ptr += len; // set file size
spi_flash_addr += len; spi_flash_write( header_position + ((char *)&hdr.fileLenComp - (char*)&hdr), (uint32_t *)&hdr.fileLenComp, sizeof(uint32_t) );
spi_flash_write( header_position + ((char *)&hdr.fileLenDecomp - (char*)&hdr), (uint32_t *)&hdr.fileLenDecomp, sizeof(uint32_t) );
} }
break; break;
case FILE_DONE: case FILE_UPLOAD_DONE:
{ {
if( html_header_len != 0 ) // write the termination block
{
// write the terminating block on esp-fs EspFsHeader hdr;
int spi_flash_addr = getUserPageSectionStart() + upload_offset + position; hdr.magic = ESPFS_MAGIC;
hdr.flags = 1;
hdr.compression = 0;
hdr.nameLen = 0;
hdr.fileLenComp = hdr.fileLenDecomp = 0;
if( webServerSetupWriteFlash( upload_position, (uint32_t *)(&hdr), sizeof(EspFsHeader) ) )
return 1;
upload_position += sizeof(EspFsHeader);
uint32_t pad = 0;
uint8_t pad_cnt = (4 - position) & 3;
if( pad_cnt )
spi_flash_write( spi_flash_addr, &pad, pad_cnt );
spi_flash_addr += pad_cnt;
// create ESPFS image
EspFsHeader hdr;
hdr.magic = ESPFS_MAGIC;
hdr.flags = 1;
hdr.compression = 0;
hdr.nameLen = 0;
hdr.fileLenComp = hdr.fileLenDecomp = 0;
spi_flash_write( spi_flash_addr, (uint32_t *)(&hdr), sizeof(EspFsHeader) );
uint32_t totallen = html_header_len + position;
// restore ESPFS magic
spi_flash_write( (int)getUserPageSectionStart(), (uint32_t *)&hdr.magic, sizeof(uint32_t) );
// set file size
spi_flash_write( (int)getUserPageSectionStart() + 8, &totallen, sizeof(uint32_t) );
spi_flash_write( (int)getUserPageSectionStart() + 12, &totallen, sizeof(uint32_t) );
}
else
{
// set espfs magic (set it valid)
uint32_t magic = ESPFS_MAGIC;
spi_flash_write( (int)getUserPageSectionStart(), (uint32_t *)&magic, sizeof(uint32_t) );
}
WEB_Init(); // reload the content WEB_Init(); // reload the content
} }
break; break;

@ -8,7 +8,7 @@
the measurement data.</p> the measurement data.</p>
<form method="post" action="web-server/upload" name="submit" enctype="multipart/form-data" onSubmit="return onSubmit()"> <form method="post" action="web-server/upload" name="submit" enctype="multipart/form-data" onSubmit="return onSubmit()">
The custom web page to upload: <input type="file" name="webpage"> The custom web page to upload: <input type="file" name="webpage" multiple>
<input type="submit" name="submit" value="Submit"> <input type="submit" name="submit" value="Submit">
</form> </form>

@ -4,7 +4,7 @@
#include "multipart.h" #include "multipart.h"
#include "cgi.h" #include "cgi.h"
#define BOUNDARY_SIZE 100 #define BOUNDARY_SIZE 128
typedef enum { typedef enum {
STATE_SEARCH_BOUNDARY = 0, // state: searching multipart boundary STATE_SEARCH_BOUNDARY = 0, // state: searching multipart boundary
@ -227,6 +227,7 @@ int ICACHE_FLASH_ATTR multipartProcessData(MultipartCtx * context, char * bounda
context->boundaryBufferPtr -= dataSize; context->boundaryBufferPtr -= dataSize;
os_memcpy(context->boundaryBuffer, context->boundaryBuffer + dataSize, context->boundaryBufferPtr); os_memcpy(context->boundaryBuffer, context->boundaryBuffer + dataSize, context->boundaryBufferPtr);
} }
return 0; return 0;
} }
@ -253,6 +254,9 @@ int ICACHE_FLASH_ATTR multipartProcess(MultipartCtx * context, HttpdConnData * c
context->state = STATE_SEARCH_BOUNDARY; context->state = STATE_SEARCH_BOUNDARY;
multipartAllocBoundaryBuffer(context); multipartAllocBoundaryBuffer(context);
if( context->callBack( FILE_UPLOAD_START, NULL, 0, context->position ) ) // start uploading files
context->state = STATE_ERROR;
} }
if( context->state != STATE_ERROR ) if( context->state != STATE_ERROR )
@ -281,6 +285,8 @@ int ICACHE_FLASH_ATTR multipartProcess(MultipartCtx * context, HttpdConnData * c
// this is the last package, process the remaining data // this is the last package, process the remaining data
if( multipartProcessData(context, post->multipartBoundary, NULL, 0, 1) ) if( multipartProcessData(context, post->multipartBoundary, NULL, 0, 1) )
context->state = STATE_ERROR; context->state = STATE_ERROR;
else if( context->callBack( FILE_UPLOAD_DONE, NULL, 0, context->position ) ) // done with files
context->state = STATE_ERROR;
} }
multipartFreeBoundaryBuffer( context ); multipartFreeBoundaryBuffer( context );

@ -4,9 +4,11 @@
#include <httpd.h> #include <httpd.h>
typedef enum { typedef enum {
FILE_START, // multipart: the start of a new file FILE_UPLOAD_START, // multipart: uploading files started
FILE_DATA, // multipart: file data FILE_START, // multipart: the start of a new file (can be more)
FILE_DONE, // multipart: file end FILE_DATA, // multipart: file data
FILE_DONE, // multipart: file end
FILE_UPLOAD_DONE, // multipart: finished for all files
} MultipartCmd; } MultipartCmd;
// multipart callback // multipart callback

Loading…
Cancel
Save