Add object-based file API to dev-esp32 branch (#2524)
* #2521: Add object-based file API to dev-esp32 branch Merged in from master@c708828bbe853764b9de58fb8113a70f5a24002d minus some VFS changes and similar. * Updated file docs from master Omitting: * file.mount() * reference to rtctime.epoch2cal() * file.stat() As these are not available in dev-esp32 branch Unsure about whether "directories are not supported" is still true
This commit is contained in:
parent
7ded3960eb
commit
48b9518d76
|
@ -2,14 +2,29 @@
|
|||
|
||||
#include "module.h"
|
||||
#include "lauxlib.h"
|
||||
#include "lmem.h"
|
||||
#include "platform.h"
|
||||
|
||||
#include "vfs.h"
|
||||
#include <string.h>
|
||||
|
||||
#define FILE_READ_CHUNK 1024
|
||||
|
||||
// use this time/date in absence of a timestamp
|
||||
#define FILE_TIMEDEF_YEAR 1970
|
||||
#define FILE_TIMEDEF_MON 01
|
||||
#define FILE_TIMEDEF_DAY 01
|
||||
#define FILE_TIMEDEF_HOUR 00
|
||||
#define FILE_TIMEDEF_MIN 00
|
||||
#define FILE_TIMEDEF_SEC 00
|
||||
|
||||
static int file_fd = 0;
|
||||
static int file_fd_ref = LUA_NOREF;
|
||||
static int rtc_cb_ref = LUA_NOREF;
|
||||
|
||||
typedef struct _file_fd_ud {
|
||||
int fd;
|
||||
} file_fd_ud;
|
||||
|
||||
static void table2tm( lua_State *L, vfs_time *tm )
|
||||
{
|
||||
|
@ -23,12 +38,12 @@ static void table2tm( lua_State *L, vfs_time *tm )
|
|||
lua_getfield( L, idx, "min" );
|
||||
lua_getfield( L, idx, "sec" );
|
||||
|
||||
tm->year = luaL_optint( L, ++idx, 2016 );
|
||||
tm->mon = luaL_optint( L, ++idx, 6 );
|
||||
tm->day = luaL_optint( L, ++idx, 21 );
|
||||
tm->hour = luaL_optint( L, ++idx, 0 );
|
||||
tm->min = luaL_optint( L, ++idx, 0 );
|
||||
tm->sec = luaL_optint( L, ++idx, 0 );
|
||||
tm->year = luaL_optint( L, ++idx, FILE_TIMEDEF_YEAR );
|
||||
tm->mon = luaL_optint( L, ++idx, FILE_TIMEDEF_MON );
|
||||
tm->day = luaL_optint( L, ++idx, FILE_TIMEDEF_DAY );
|
||||
tm->hour = luaL_optint( L, ++idx, FILE_TIMEDEF_HOUR );
|
||||
tm->min = luaL_optint( L, ++idx, FILE_TIMEDEF_MIN );
|
||||
tm->sec = luaL_optint( L, ++idx, FILE_TIMEDEF_SEC );
|
||||
|
||||
// remove items from stack
|
||||
lua_pop( L, 6 );
|
||||
|
@ -90,10 +105,44 @@ static int file_on(lua_State *L)
|
|||
// Lua: close()
|
||||
static int file_close( lua_State* L )
|
||||
{
|
||||
if(file_fd){
|
||||
vfs_close(file_fd);
|
||||
file_fd = 0;
|
||||
file_fd_ud *ud;
|
||||
|
||||
if (lua_type( L, 1 ) != LUA_TUSERDATA) {
|
||||
// fall back to last opened file
|
||||
if (file_fd_ref != LUA_NOREF) {
|
||||
lua_rawgeti( L, LUA_REGISTRYINDEX, file_fd_ref );
|
||||
// top of stack is now default file descriptor
|
||||
ud = (file_fd_ud *)luaL_checkudata(L, -1, "file.obj");
|
||||
lua_pop( L, 1 );
|
||||
} else {
|
||||
// no default file currently opened
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
ud = (file_fd_ud *)luaL_checkudata(L, 1, "file.obj");
|
||||
}
|
||||
|
||||
// unref default file descriptor
|
||||
luaL_unref( L, LUA_REGISTRYINDEX, file_fd_ref );
|
||||
file_fd_ref = LUA_NOREF;
|
||||
|
||||
if(ud->fd){
|
||||
vfs_close(ud->fd);
|
||||
// mark as closed
|
||||
ud->fd = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int file_obj_free( lua_State *L )
|
||||
{
|
||||
file_fd_ud *ud = (file_fd_ud *)luaL_checkudata(L, 1, "file.obj");
|
||||
if (ud->fd) {
|
||||
// close file if it's still open
|
||||
vfs_close(ud->fd);
|
||||
ud->fd = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -130,10 +179,10 @@ static int file_fscfg (lua_State *L)
|
|||
static int file_open( lua_State* L )
|
||||
{
|
||||
size_t len;
|
||||
if(file_fd){
|
||||
vfs_close(file_fd);
|
||||
file_fd = 0;
|
||||
}
|
||||
|
||||
// unref last file descriptor to allow gc'ing if not kept by user script
|
||||
luaL_unref( L, LUA_REGISTRYINDEX, file_fd_ref );
|
||||
file_fd_ref = LUA_NOREF;
|
||||
|
||||
const char *fname = luaL_checklstring( L, 1, &len );
|
||||
const char *basename = vfs_basename( fname );
|
||||
|
@ -146,7 +195,14 @@ static int file_open( lua_State* L )
|
|||
if(!file_fd){
|
||||
lua_pushnil(L);
|
||||
} else {
|
||||
lua_pushboolean(L, 1);
|
||||
file_fd_ud *ud = (file_fd_ud *) lua_newuserdata( L, sizeof( file_fd_ud ) );
|
||||
ud->fd = file_fd;
|
||||
luaL_getmetatable( L, "file.obj" );
|
||||
lua_setmetatable( L, -2 );
|
||||
|
||||
// store reference to opened file
|
||||
lua_pushvalue( L, -1 );
|
||||
file_fd_ref = luaL_ref( L, LUA_REGISTRYINDEX );
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
@ -170,19 +226,36 @@ static int file_list( lua_State* L )
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int get_file_obj( lua_State *L, int *argpos )
|
||||
{
|
||||
if (lua_type( L, 1 ) == LUA_TUSERDATA) {
|
||||
file_fd_ud *ud = (file_fd_ud *)luaL_checkudata(L, 1, "file.obj");
|
||||
*argpos = 2;
|
||||
return ud->fd;
|
||||
} else {
|
||||
*argpos = 1;
|
||||
return file_fd;
|
||||
}
|
||||
}
|
||||
|
||||
#define GET_FILE_OBJ int argpos; \
|
||||
int fd = get_file_obj( L, &argpos );
|
||||
|
||||
static int file_seek (lua_State *L)
|
||||
{
|
||||
GET_FILE_OBJ;
|
||||
|
||||
static const int mode[] = {VFS_SEEK_SET, VFS_SEEK_CUR, VFS_SEEK_END};
|
||||
static const char *const modenames[] = {"set", "cur", "end", NULL};
|
||||
if(!file_fd)
|
||||
if(!fd)
|
||||
return luaL_error(L, "open a file first");
|
||||
int op = luaL_checkoption(L, 1, "cur", modenames);
|
||||
long offset = luaL_optlong(L, 2, 0);
|
||||
op = vfs_lseek(file_fd, offset, mode[op]);
|
||||
int op = luaL_checkoption(L, argpos, "cur", modenames);
|
||||
long offset = luaL_optlong(L, ++argpos, 0);
|
||||
op = vfs_lseek(fd, offset, mode[op]);
|
||||
if (op < 0)
|
||||
lua_pushnil(L); /* error */
|
||||
else
|
||||
lua_pushinteger(L, vfs_tell(file_fd));
|
||||
lua_pushinteger(L, vfs_tell(fd));
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -210,7 +283,6 @@ static int file_remove( lua_State* L )
|
|||
const char *fname = luaL_checklstring( L, 1, &len );
|
||||
const char *basename = vfs_basename( fname );
|
||||
luaL_argcheck(L, strlen(basename) <= CONFIG_FS_OBJ_NAME_LEN && strlen(fname) == len, 1, "filename invalid");
|
||||
file_close(L);
|
||||
vfs_remove((char *)fname);
|
||||
return 0;
|
||||
}
|
||||
|
@ -218,9 +290,11 @@ static int file_remove( lua_State* L )
|
|||
// Lua: flush()
|
||||
static int file_flush( lua_State* L )
|
||||
{
|
||||
if(!file_fd)
|
||||
GET_FILE_OBJ;
|
||||
|
||||
if(!fd)
|
||||
return luaL_error(L, "open a file first");
|
||||
if(vfs_flush(file_fd) == 0)
|
||||
if(vfs_flush(fd) == 0)
|
||||
lua_pushboolean(L, 1);
|
||||
else
|
||||
lua_pushnil(L);
|
||||
|
@ -231,10 +305,6 @@ static int file_flush( lua_State* L )
|
|||
static int file_rename( lua_State* L )
|
||||
{
|
||||
size_t len;
|
||||
if(file_fd){
|
||||
vfs_close(file_fd);
|
||||
file_fd = 0;
|
||||
}
|
||||
|
||||
const char *oldname = luaL_checklstring( L, 1, &len );
|
||||
const char *basename = vfs_basename( oldname );
|
||||
|
@ -253,38 +323,65 @@ static int file_rename( lua_State* L )
|
|||
}
|
||||
|
||||
// g_read()
|
||||
static int file_g_read( lua_State* L, int n, int16_t end_char )
|
||||
static int file_g_read( lua_State* L, int n, int16_t end_char, int fd )
|
||||
{
|
||||
if(n <= 0 || n > LUAL_BUFFERSIZE)
|
||||
n = LUAL_BUFFERSIZE;
|
||||
static char *heap_mem = NULL;
|
||||
// free leftover memory
|
||||
if (heap_mem) {
|
||||
luaM_free(L, heap_mem);
|
||||
heap_mem = NULL;
|
||||
}
|
||||
|
||||
if(n <= 0)
|
||||
n = FILE_READ_CHUNK;
|
||||
|
||||
if(end_char < 0 || end_char >255)
|
||||
end_char = EOF;
|
||||
|
||||
luaL_Buffer b;
|
||||
if(!file_fd)
|
||||
|
||||
if(!fd)
|
||||
return luaL_error(L, "open a file first");
|
||||
|
||||
luaL_buffinit(L, &b);
|
||||
char *p = luaL_prepbuffer(&b);
|
||||
char *p;
|
||||
int i;
|
||||
|
||||
n = vfs_read(file_fd, p, n);
|
||||
if (n > LUAL_BUFFERSIZE) {
|
||||
// get buffer from heap
|
||||
p = heap_mem = luaM_malloc(L, n);
|
||||
} else {
|
||||
// small chunks go onto the stack
|
||||
p = alloca(n);
|
||||
}
|
||||
|
||||
n = vfs_read(fd, p, n);
|
||||
// bypass search if no end character provided
|
||||
if (n > 0 && end_char != EOF) {
|
||||
for (i = 0; i < n; ++i)
|
||||
if (p[i] == end_char)
|
||||
{
|
||||
++i;
|
||||
break;
|
||||
}
|
||||
|
||||
if(i==0){
|
||||
luaL_pushresult(&b); /* close buffer */
|
||||
return (lua_objlen(L, -1) > 0); /* check whether read something */
|
||||
} else {
|
||||
i = n;
|
||||
}
|
||||
|
||||
vfs_lseek(file_fd, -(n - i), VFS_SEEK_CUR);
|
||||
luaL_addsize(&b, i);
|
||||
luaL_pushresult(&b); /* close buffer */
|
||||
return 1; /* read at least an `eol' */
|
||||
if (i == 0 || n == VFS_RES_ERR) {
|
||||
if (heap_mem) {
|
||||
luaM_free(L, heap_mem);
|
||||
heap_mem = NULL;
|
||||
}
|
||||
lua_pushnil(L);
|
||||
return 1;
|
||||
}
|
||||
|
||||
vfs_lseek(fd, -(n - i), VFS_SEEK_CUR);
|
||||
lua_pushlstring(L, p, i);
|
||||
if (heap_mem) {
|
||||
luaM_free(L, heap_mem);
|
||||
heap_mem = NULL;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Lua: read()
|
||||
|
@ -293,42 +390,46 @@ static int file_g_read( lua_State* L, int n, int16_t end_char )
|
|||
// file.read('q') will read until 'q' or EOF is reached.
|
||||
static int file_read( lua_State* L )
|
||||
{
|
||||
unsigned need_len = LUAL_BUFFERSIZE;
|
||||
unsigned need_len = FILE_READ_CHUNK;
|
||||
int16_t end_char = EOF;
|
||||
size_t el;
|
||||
if( lua_type( L, 1 ) == LUA_TNUMBER )
|
||||
|
||||
GET_FILE_OBJ;
|
||||
|
||||
if( lua_type( L, argpos ) == LUA_TNUMBER )
|
||||
{
|
||||
need_len = ( unsigned )luaL_checkinteger( L, 1 );
|
||||
if( need_len > LUAL_BUFFERSIZE ){
|
||||
need_len = LUAL_BUFFERSIZE;
|
||||
need_len = ( unsigned )luaL_checkinteger( L, argpos );
|
||||
}
|
||||
}
|
||||
else if(lua_isstring(L, 1))
|
||||
else if(lua_isstring(L, argpos))
|
||||
{
|
||||
const char *end = luaL_checklstring( L, 1, &el );
|
||||
const char *end = luaL_checklstring( L, argpos, &el );
|
||||
if(el!=1){
|
||||
return luaL_error( L, "wrong arg range" );
|
||||
}
|
||||
end_char = (int16_t)end[0];
|
||||
}
|
||||
|
||||
return file_g_read(L, need_len, end_char);
|
||||
return file_g_read(L, need_len, end_char, fd);
|
||||
}
|
||||
|
||||
// Lua: readline()
|
||||
static int file_readline( lua_State* L )
|
||||
{
|
||||
return file_g_read(L, LUAL_BUFFERSIZE, '\n');
|
||||
GET_FILE_OBJ;
|
||||
|
||||
return file_g_read(L, FILE_READ_CHUNK, '\n', fd);
|
||||
}
|
||||
|
||||
// Lua: write("string")
|
||||
static int file_write( lua_State* L )
|
||||
{
|
||||
if(!file_fd)
|
||||
GET_FILE_OBJ;
|
||||
|
||||
if(!fd)
|
||||
return luaL_error(L, "open a file first");
|
||||
size_t l, rl;
|
||||
const char *s = luaL_checklstring(L, 1, &l);
|
||||
rl = vfs_write(file_fd, s, l);
|
||||
const char *s = luaL_checklstring(L, argpos, &l);
|
||||
rl = vfs_write(fd, s, l);
|
||||
if(rl==l)
|
||||
lua_pushboolean(L, 1);
|
||||
else
|
||||
|
@ -339,13 +440,15 @@ static int file_write( lua_State* L )
|
|||
// Lua: writeline("string")
|
||||
static int file_writeline( lua_State* L )
|
||||
{
|
||||
if(!file_fd)
|
||||
GET_FILE_OBJ;
|
||||
|
||||
if(!fd)
|
||||
return luaL_error(L, "open a file first");
|
||||
size_t l, rl;
|
||||
const char *s = luaL_checklstring(L, 1, &l);
|
||||
rl = vfs_write(file_fd, s, l);
|
||||
const char *s = luaL_checklstring(L, argpos, &l);
|
||||
rl = vfs_write(fd, s, l);
|
||||
if(rl==l){
|
||||
rl = vfs_write(file_fd, "\n", 1);
|
||||
rl = vfs_write(fd, "\n", 1);
|
||||
if(rl==1)
|
||||
lua_pushboolean(L, 1);
|
||||
else
|
||||
|
@ -390,6 +493,20 @@ static int file_chdir( lua_State *L )
|
|||
}
|
||||
#endif
|
||||
|
||||
static const LUA_REG_TYPE file_obj_map[] =
|
||||
{
|
||||
{ LSTRKEY( "close" ), LFUNCVAL( file_close ) },
|
||||
{ LSTRKEY( "read" ), LFUNCVAL( file_read ) },
|
||||
{ LSTRKEY( "readline" ), LFUNCVAL( file_readline ) },
|
||||
{ LSTRKEY( "write" ), LFUNCVAL( file_write ) },
|
||||
{ LSTRKEY( "writeline" ), LFUNCVAL( file_writeline ) },
|
||||
{ LSTRKEY( "seek" ), LFUNCVAL( file_seek ) },
|
||||
{ LSTRKEY( "flush" ), LFUNCVAL( file_flush ) },
|
||||
{ LSTRKEY( "__gc" ), LFUNCVAL( file_obj_free ) },
|
||||
{ LSTRKEY( "__index" ), LROVAL( file_obj_map ) },
|
||||
{ LNILKEY, LNILVAL }
|
||||
};
|
||||
|
||||
// Module function map
|
||||
static const LUA_REG_TYPE file_map[] = {
|
||||
{ LSTRKEY( "list" ), LFUNCVAL( file_list ) },
|
||||
|
@ -416,4 +533,9 @@ static const LUA_REG_TYPE file_map[] = {
|
|||
{ LNILKEY, LNILVAL }
|
||||
};
|
||||
|
||||
NODEMCU_MODULE(FILE, "file", file_map, NULL);
|
||||
int luaopen_file( lua_State *L ) {
|
||||
luaL_rometatable( L, "file.obj", (void *)file_obj_map );
|
||||
return 0;
|
||||
}
|
||||
|
||||
NODEMCU_MODULE(FILE, "file", file_map, luaopen_file);
|
||||
|
|
|
@ -7,9 +7,7 @@ The file module provides access to the file system and its individual files.
|
|||
|
||||
The file system is a flat file system, with no notion of subdirectories/folders.
|
||||
|
||||
Only one file can be open at any given time.
|
||||
|
||||
Besides the SPIFFS file system on internal flash, this module can also access FAT partitions on an external SD card is [FatFS is enabled](../sdcard.md).
|
||||
Besides the SPIFFS file system on internal flash, this module can also access FAT partitions on an external SD card if [FatFS is enabled](../sdcard.md).
|
||||
|
||||
```lua
|
||||
-- open file in flash:
|
||||
|
@ -34,6 +32,10 @@ Change current directory (and drive). This will be used when no drive/directory
|
|||
|
||||
Current directory defaults to the root of internal SPIFFS (`/FLASH`) after system start.
|
||||
|
||||
!!! note
|
||||
|
||||
Function is only available when [FatFS support](../sdcard.md#enabling-fatfs) is compiled into the firmware.
|
||||
|
||||
#### Syntax
|
||||
`file.chdir(dir)`
|
||||
|
||||
|
@ -43,30 +45,6 @@ Current directory defaults to the root of internal SPIFFS (`/FLASH`) after syste
|
|||
#### Returns
|
||||
`true` on success, `false` otherwise
|
||||
|
||||
## file.close()
|
||||
|
||||
Closes the open file, if any.
|
||||
|
||||
#### Syntax
|
||||
`file.close()`
|
||||
|
||||
#### Parameters
|
||||
none
|
||||
|
||||
#### Returns
|
||||
`nil`
|
||||
|
||||
#### Example
|
||||
```lua
|
||||
-- open 'init.lua', print the first line.
|
||||
if file.open("init.lua", "r") then
|
||||
print(file.readline())
|
||||
file.close()
|
||||
end
|
||||
```
|
||||
#### See also
|
||||
[`file.open()`](#fileopen)
|
||||
|
||||
## file.exists()
|
||||
|
||||
Determines whether the specified file exists.
|
||||
|
@ -95,39 +73,13 @@ end
|
|||
#### See also
|
||||
[`file.list()`](#filelist)
|
||||
|
||||
## file.flush()
|
||||
|
||||
Flushes any pending writes to the file system, ensuring no data is lost on a restart. Closing the open file using [`file.close()`](#fileclose) performs an implicit flush as well.
|
||||
|
||||
#### Syntax
|
||||
`file.flush()`
|
||||
|
||||
#### Parameters
|
||||
none
|
||||
|
||||
#### Returns
|
||||
`nil`
|
||||
|
||||
#### Example
|
||||
```lua
|
||||
-- open 'init.lua' in 'a+' mode
|
||||
if file.open("init.lua", "a+") then
|
||||
-- write 'foo bar' to the end of the file
|
||||
file.write('foo bar')
|
||||
file.flush()
|
||||
-- write 'baz' too
|
||||
file.write('baz')
|
||||
file.close()
|
||||
end
|
||||
```
|
||||
#### See also
|
||||
[`file.close()`](#fileclose)
|
||||
|
||||
## file.format()
|
||||
|
||||
Format the file system. Completely erases any existing file system and writes a new one. Depending on the size of the flash chip in the ESP, this may take several seconds.
|
||||
|
||||
Not supported for SD cards.
|
||||
!!! note
|
||||
|
||||
Function is not supported for SD cards.
|
||||
|
||||
#### Syntax
|
||||
`file.format()`
|
||||
|
@ -145,7 +97,9 @@ none
|
|||
|
||||
Returns the flash address and physical size of the file system area, in bytes.
|
||||
|
||||
Not supported for SD cards.
|
||||
!!! note
|
||||
|
||||
Function is not supported for SD cards.
|
||||
|
||||
#### Syntax
|
||||
`file.fscfg()`
|
||||
|
@ -211,6 +165,7 @@ end
|
|||
Registers callback functions.
|
||||
|
||||
Trigger events are:
|
||||
|
||||
- `rtc` deliver current date & time to the file system. Function is expected to return a table containing the fields `year`, `mon`, `day`, `hour`, `min`, `sec` of current date and time. Not supported for internal flash.
|
||||
|
||||
#### Syntax
|
||||
|
@ -255,7 +210,7 @@ When done with the file, it must be closed using `file.close()`.
|
|||
- "a+": append update mode, previous data is preserved, writing is only allowed at the end of file
|
||||
|
||||
#### Returns
|
||||
`nil` if file not opened, or not exists (read modes). `true` if file opened ok.
|
||||
file object if file opened ok. `nil` if file not opened, or not exists (read modes).
|
||||
|
||||
#### Example
|
||||
```lua
|
||||
|
@ -265,70 +220,19 @@ if file.open("init.lua", "r") then
|
|||
file.close()
|
||||
end
|
||||
```
|
||||
#### See also
|
||||
- [`file.close()`](#fileclose)
|
||||
- [`file.readline()`](#filereadline)
|
||||
|
||||
## file.read()
|
||||
|
||||
Read content from the open file.
|
||||
|
||||
#### Syntax
|
||||
`file.read([n_or_str])`
|
||||
|
||||
#### Parameters
|
||||
- `n_or_str`:
|
||||
- if nothing passed in, read up to `LUAL_BUFFERSIZE` bytes (default 1024) or the entire file (whichever is smaller)
|
||||
- if passed a number n, then read the file until the lesser of `n` bytes, `LUAL_BUFFERSIZE` bytes, or EOF is reached. Specifying a number larger than the buffer size will read the buffer size.
|
||||
- if passed a string `str`, then read until `str` appears next in the file, `LUAL_BUFFERSIZE` bytes have been read, or EOF is reached
|
||||
|
||||
#### Returns
|
||||
File content as a string, or nil when EOF
|
||||
|
||||
#### Example
|
||||
#### Example (object model)
|
||||
```lua
|
||||
-- print the first line of 'init.lua'
|
||||
if file.open("init.lua", "r") then
|
||||
print(file.read('\n'))
|
||||
file.close()
|
||||
end
|
||||
|
||||
-- print the first 5 bytes of 'init.lua'
|
||||
if file.open("init.lua", "r") then
|
||||
print(file.read(5))
|
||||
file.close()
|
||||
-- open 'init.lua', print the first line.
|
||||
fd = file.open("init.lua", "r")
|
||||
if fd then
|
||||
print(fd:readline())
|
||||
fd:close(); fd = nil
|
||||
end
|
||||
```
|
||||
|
||||
#### See also
|
||||
- [`file.open()`](#fileopen)
|
||||
- [`file.readline()`](#filereadline)
|
||||
|
||||
## file.readline()
|
||||
|
||||
Read the next line from the open file. Lines are defined as zero or more bytes ending with a EOL ('\n') byte. If the next line is longer than `LUAL_BUFFERSIZE`, this function only returns the first `LUAL_BUFFERSIZE` bytes (this is 1024 bytes by default).
|
||||
|
||||
#### Syntax
|
||||
`file.readline()`
|
||||
|
||||
#### Parameters
|
||||
none
|
||||
|
||||
#### Returns
|
||||
File content in string, line by line, including EOL('\n'). Return `nil` when EOF.
|
||||
|
||||
#### Example
|
||||
```lua
|
||||
-- print the first line of 'init.lua'
|
||||
if file.open("init.lua", "r") then
|
||||
print(file.readline())
|
||||
file.close()
|
||||
end
|
||||
```
|
||||
#### See also
|
||||
- [`file.open()`](#fileopen)
|
||||
- [`file.close()`](#fileclose)
|
||||
- [`file.read()`](#filereade)
|
||||
- [`file.close()`](#fileclose-fileobjclose)
|
||||
- [`file.readline()`](#filereadline-fileobjreadline)
|
||||
|
||||
## file.remove()
|
||||
|
||||
|
@ -373,12 +277,183 @@ Renames a file. If a file is currently open, it will be closed first.
|
|||
file.rename("temp.lua","init.lua")
|
||||
```
|
||||
|
||||
## file.seek()
|
||||
# File access functions
|
||||
|
||||
The `file` module provides several functions to access the content of a file after it has been opened with [`file.open()`](#fileopen). They can be used as part of a basic model or an object model:
|
||||
|
||||
## Basic model
|
||||
In the basic model there is max one file opened at a time. The file access functions operate on this file per default. If another file is opened, the previous default file needs to be closed beforehand.
|
||||
|
||||
```lua
|
||||
-- open 'init.lua', print the first line.
|
||||
if file.open("init.lua", "r") then
|
||||
print(file.readline())
|
||||
file.close()
|
||||
end
|
||||
```
|
||||
|
||||
## Object model
|
||||
Files are represented by file objects which are created by `file.open()`. File access functions are available as methods of this object, and multiple file objects can coexist.
|
||||
|
||||
```lua
|
||||
src = file.open("init.lua", "r")
|
||||
if src then
|
||||
dest = file.open("copy.lua", "w")
|
||||
if dest then
|
||||
local line
|
||||
repeat
|
||||
line = src:read()
|
||||
if line then
|
||||
dest:write(line)
|
||||
end
|
||||
until line == nil
|
||||
dest:close(); dest = nil
|
||||
end
|
||||
src:close(); dest = nil
|
||||
end
|
||||
```
|
||||
|
||||
!!! Attention
|
||||
|
||||
It is recommended to use only one single model within the application. Concurrent use of both models can yield unpredictable behavior: Closing the default file from basic model will also close the correspoding file object. Closing a file from object model will also close the default file if they are the same file.
|
||||
|
||||
!!! Note
|
||||
|
||||
The maximum number of open files on SPIFFS is determined at compile time by `SPIFFS_MAX_OPEN_FILES` in `user_config.h`.
|
||||
|
||||
## file.close(), file.obj:close()
|
||||
|
||||
Closes the open file, if any.
|
||||
|
||||
#### Syntax
|
||||
`file.close()`
|
||||
|
||||
`fd:close()`
|
||||
|
||||
#### Parameters
|
||||
none
|
||||
|
||||
#### Returns
|
||||
`nil`
|
||||
|
||||
#### See also
|
||||
[`file.open()`](#fileopen)
|
||||
|
||||
## file.flush(), file.obj:flush()
|
||||
|
||||
Flushes any pending writes to the file system, ensuring no data is lost on a restart. Closing the open file using [`file.close()` / `fd:close()`](#fileclose-fileobjclose) performs an implicit flush as well.
|
||||
|
||||
#### Syntax
|
||||
`file.flush()`
|
||||
|
||||
`fd:flush()`
|
||||
|
||||
#### Parameters
|
||||
none
|
||||
|
||||
#### Returns
|
||||
`nil`
|
||||
|
||||
#### Example (basic model)
|
||||
```lua
|
||||
-- open 'init.lua' in 'a+' mode
|
||||
if file.open("init.lua", "a+") then
|
||||
-- write 'foo bar' to the end of the file
|
||||
file.write('foo bar')
|
||||
file.flush()
|
||||
-- write 'baz' too
|
||||
file.write('baz')
|
||||
file.close()
|
||||
end
|
||||
```
|
||||
|
||||
#### See also
|
||||
[`file.close()` / `file.obj:close()`](#fileclose-fileobjclose)
|
||||
|
||||
## file.read(), file.obj:read()
|
||||
|
||||
Read content from the open file.
|
||||
|
||||
!!! note
|
||||
|
||||
The function temporarily allocates 2 * (number of requested bytes) on the heap for buffering and processing the read data. Default chunk size (`FILE_READ_CHUNK`) is 1024 bytes and is regarded to be safe. Pushing this by 4x or more can cause heap overflows depending on the application. Consider this when selecting a value for parameter `n_or_char`.
|
||||
|
||||
#### Syntax
|
||||
`file.read([n_or_char])`
|
||||
|
||||
`fd:read([n_or_char])`
|
||||
|
||||
#### Parameters
|
||||
- `n_or_char`:
|
||||
- if nothing passed in, then read up to `FILE_READ_CHUNK` bytes or the entire file (whichever is smaller).
|
||||
- if passed a number `n`, then read up to `n` bytes or the entire file (whichever is smaller).
|
||||
- if passed a string containing the single character `char`, then read until `char` appears next in the file, `FILE_READ_CHUNK` bytes have been read, or EOF is reached.
|
||||
|
||||
#### Returns
|
||||
File content as a string, or nil when EOF
|
||||
|
||||
#### Example (basic model)
|
||||
```lua
|
||||
-- print the first line of 'init.lua'
|
||||
if file.open("init.lua", "r") then
|
||||
print(file.read('\n'))
|
||||
file.close()
|
||||
end
|
||||
```
|
||||
|
||||
#### Example (object model)
|
||||
```lua
|
||||
-- print the first 5 bytes of 'init.lua'
|
||||
fd = file.open("init.lua", "r")
|
||||
if fd then
|
||||
print(fd:read(5))
|
||||
fd:close(); fd = nil
|
||||
end
|
||||
```
|
||||
|
||||
#### See also
|
||||
- [`file.open()`](#fileopen)
|
||||
- [`file.readline()` / `file.obj:readline()`](#filereadline-fileobjreadline)
|
||||
|
||||
## file.readline(), file.obj:readline()
|
||||
|
||||
Read the next line from the open file. Lines are defined as zero or more bytes ending with a EOL ('\n') byte. If the next line is longer than 1024, this function only returns the first 1024 bytes.
|
||||
|
||||
#### Syntax
|
||||
`file.readline()`
|
||||
|
||||
`fd:readline()`
|
||||
|
||||
#### Parameters
|
||||
none
|
||||
|
||||
#### Returns
|
||||
File content in string, line by line, including EOL('\n'). Return `nil` when EOF.
|
||||
|
||||
#### Example (basic model)
|
||||
```lua
|
||||
-- print the first line of 'init.lua'
|
||||
if file.open("init.lua", "r") then
|
||||
print(file.readline())
|
||||
file.close()
|
||||
end
|
||||
```
|
||||
|
||||
#### See also
|
||||
- [`file.open()`](#fileopen)
|
||||
- [`file.close()` / `file.obj:close()`](#fileclose-fileobjclose)
|
||||
- [`file.read()` / `file.obj:read()`](#fileread-fileobjread)
|
||||
|
||||
|
||||
## file.seek(), file.obj:seek()
|
||||
|
||||
Sets and gets the file position, measured from the beginning of the file, to the position given by offset plus a base specified by the string whence.
|
||||
|
||||
#### Syntax
|
||||
`file.seek([whence [, offset]])`
|
||||
|
||||
`fd:seek([whence [, offset]])`
|
||||
|
||||
#### Parameters
|
||||
- `whence`
|
||||
- "set": base is position 0 (beginning of the file)
|
||||
|
@ -391,7 +466,7 @@ If no parameters are given, the function simply returns the current file offset.
|
|||
#### Returns
|
||||
the resulting file position, or `nil` on error
|
||||
|
||||
#### Example
|
||||
#### Example (basic model)
|
||||
```lua
|
||||
if file.open("init.lua", "r") then
|
||||
-- skip the first 5 bytes of the file
|
||||
|
@ -403,20 +478,22 @@ end
|
|||
#### See also
|
||||
[`file.open()`](#fileopen)
|
||||
|
||||
## file.write()
|
||||
## file.write(), file.obj:write()
|
||||
|
||||
Write a string to the open file.
|
||||
|
||||
#### Syntax
|
||||
`file.write(string)`
|
||||
|
||||
`fd:write(string)`
|
||||
|
||||
#### Parameters
|
||||
`string` content to be write to file
|
||||
|
||||
#### Returns
|
||||
`true` if the write is ok, `nil` on error
|
||||
|
||||
#### Example
|
||||
#### Example (basic model)
|
||||
```lua
|
||||
-- open 'init.lua' in 'a+' mode
|
||||
if file.open("init.lua", "a+") then
|
||||
|
@ -426,24 +503,37 @@ if file.open("init.lua", "a+") then
|
|||
end
|
||||
```
|
||||
|
||||
#### Example (object model)
|
||||
```lua
|
||||
-- open 'init.lua' in 'a+' mode
|
||||
fd = file.open("init.lua", "a+")
|
||||
if fd then
|
||||
-- write 'foo bar' to the end of the file
|
||||
fd:write('foo bar')
|
||||
fd:close()
|
||||
end
|
||||
```
|
||||
|
||||
#### See also
|
||||
- [`file.open()`](#fileopen)
|
||||
- [`file.writeline()`](#filewriteline)
|
||||
- [`file.writeline()` / `file.obj:writeline()`](#filewriteline-fileobjwriteline)
|
||||
|
||||
## file.writeline()
|
||||
## file.writeline(), file.obj:writeline()
|
||||
|
||||
Write a string to the open file and append '\n' at the end.
|
||||
|
||||
#### Syntax
|
||||
`file.writeline(string)`
|
||||
|
||||
`fd:writeline(string)`
|
||||
|
||||
#### Parameters
|
||||
`string` content to be write to file
|
||||
|
||||
#### Returns
|
||||
`true` if write ok, `nil` on error
|
||||
|
||||
#### Example
|
||||
#### Example (basic model)
|
||||
```lua
|
||||
-- open 'init.lua' in 'a+' mode
|
||||
if file.open("init.lua", "a+") then
|
||||
|
@ -455,4 +545,4 @@ end
|
|||
|
||||
#### See also
|
||||
- [`file.open()`](#fileopen)
|
||||
- [`file.readline()`](#filereadline)
|
||||
- [`file.readline()` / `file.obj:readline()`](#filereadline-fileobjreadline)
|
||||
|
|
Loading…
Reference in New Issue