ESP8266开发之旅 网络篇⑬ SPIFFS——ESP8266 SPIFFS文件系统
授人以鱼不如授人以渔,目的不是为了教会你具体项目开发,而是学会学习的能力。希望大家分享给你周边需要的朋友或者同学,说不定大神成长之路有博哥的奠基石。。。
QQ技术互动交流群:ESP8266&32 物联网开发 群号622368884,不喜勿喷
一、你如果想学基于Arduino的ESP8266开发技术
一、基础篇
二、网络篇
- ESP8266开发之旅 网络篇① 认识一下Arduino Core For ESP8266
- ESP8266开发之旅 网络篇② ESP8266 工作模式与ESP8266WiFi库
- ESP8266开发之旅 网络篇③ Soft-AP——ESP8266WiFiAP库的使用
- ESP8266开发之旅 网络篇④ Station——ESP8266WiFiSTA库的使用
- ESP8266开发之旅 网络篇⑤ Scan WiFi——ESP8266WiFiScan库的使用
- ESP8266开发之旅 网络篇⑥ ESP8266WiFiGeneric——基础库
- ESP8266开发之旅 网络篇⑦ TCP Server & TCP Client
- ESP8266开发之旅 网络篇⑧ SmartConfig——一键配网
- ESP8266开发之旅 网络篇⑨ HttpClient——ESP8266HTTPClient库的使用
- ESP8266开发之旅 网络篇⑩ UDP服务
- ESP8266开发之旅 网络篇⑪ WebServer——ESP8266WebServer库的使用
- ESP8266开发之旅 网络篇⑫ 域名服务——ESP8266mDNS库
- ESP8266开发之旅 网络篇⑬ SPIFFS——ESP8266 Flash文件系统
- ESP8266开发之旅 网络篇⑭ web配网
- ESP8266开发之旅 网络篇⑮ 真正的域名服务——DNSServer
- ESP8266开发之旅 网络篇⑯ 无线更新——OTA固件更新
三、应用篇
四、高级篇
1. 前言
在前面博文关于ESP8266WiFiWebServer的例程中,大家可以发现,博主基本上都是手动拼装html内容返回,html的内容被固定写在我们的Arduino ESP代码中。
那么这样就有两点弊端:
- ESP8266代码相当臃肿
- 为了开发方便,web server网页除了自身的html内容之外,还包括一些css文件,甚至引入了JQuery库以及一些图片相关资源。如果把这些内容也直接写入到ESP8266代码中,会导致8266整体代码变大,甚至可能超过flash规定的大小;
- 业务职责分离不明确
- 一般来说,在一个开发团队中,有人负责开发ESP8266业务需求,有人负责开发WebServer网页内容,有人负责硬件部分。直接把html的内容直接写入到ESP8266代码中,就会导致业务职责混乱,并且如果要修改html内容的时候还得一个个改掉arduino的文件,也有可能改错标识符之类的。理想情况应该是,只需要更新web server的html文件就好,原来的esp8266 arduino逻辑不用更新;
基于以上两点弊端,正式引入本篇章需要研究的ESP8266 文件系统(SPI Flash FileSystem,简称为SPIFFS)。
先来看一个概念图:
这个文件系统可以帮助我们存储一些变更频率不频繁的文件例如网页、配置或者是某些固化的数据等。
其实,我们用得更多的是存储网页,将网页和相关资源(如:图片、html、css、javaScript)存入到flash的SPIFFS区域。
原理如下图:
2. FLASH存储分配
在讲解SPIFFS之前,我们来看看在Arduino环境下ESP8266的flash存储分配,请看下图:
具体可以分为几部分:
- 代码区
- 又叫做程序存储区,其中又区分为当前代码区(current Sketch),更新代码区(OTA update);
- 文件系统
- 这个就是我们这节重点讲解的SPI Flash File System,简称SPIFFS闪存文件系统。
即使文件系统与程序存储在同一个闪存芯片上,烧入新的代码也不会修改文件系统内容。这允许使用文件系统来存储Web服务器的代码数据、配置文件或内容。而这个SPIFFS文件系统的大小可以通过烧写环境来配置,目前一般有1M,2M,3M等等。博主建议如果是NodeMcu板子,可以配置成3M;
为了使用文件系统,需要把下面的头文件包含在代码中:
#include <FS.h>
- EEPROM
- 具体讲解请回顾 ESP8266开发之旅 基础篇④ ESP8266与EEPROM
- WiFi Config
- 这个区域就是我们设置WiFi模块配置的时候存储的数据。
3. SPIFFS文件系统
3.1 文件系统限制
ESP8266的文件系统实现必须满足芯片的限制,其中最重要是有限的RAM。SPIFFS之所以被ESP8266选择作为文件系统,是因为它是为小型系统专门设计的,同时是以一些简化和限制为代价的。
首先,SPIFFS不支持目录,它只存储一个“扁平化”的文件列表。但是与传统的文件系统相反,斜杠字符“/”在文件名中是允许的,因此处理目录列表的函数(例如,openDir("/website"))基本上只是过滤文件名,并保留以前缀(/website/)开始的那些文件。
然后,对于文件名,总共有32个字符限制。一个“\0”字符被保留用于c字符串终止符,因此留给我们31个可用字符长度。
综合起来,这意味着建议保持短文件名,不要使用深嵌套的目录,因为每个文件的完整路径(包括目录、“/”字符、基本名称、点和扩展名)最多只能是31个字符长度。例如,/website/images/bird_thumbnail.jpg 达到了34个字符长度,如果使用它,将导致一些问题。
警告:这个限制很容易达到,如果忽略,问题可能会被忽略,因为在编译和运行时不会出现错误信息。
3.2 文件系统文件添加方式
使用文件系统目的就是为了存储文件,那么存储文件的方式其实可以分为3种:
- 直接代码中调用FS提供的API在SPIFFS上创建文件;
- 通过 ESP8266FS 工具把文件上传到SPIFFS;
- 通过OTA Update的方式上传到SPIFFS;
本质上,无论是通过ESP8266FS或者OTA Update的方式把文件上传到SPIFFS,其底层都是通过调用FS提供的API去完成,所以我们只需要了解FS常用API即可。
4. SPIFFS库
了解一下SPIFFS文件系统常用的操作方法,以下是博主总结的百度脑图:
方法分为3大类:
- SPIFFS专用方法
- Dir对象专用方法
- File对象专用方法
4.1 SPIFFS专用方法
4.1.1 begin —— 挂载SPIFFS文件系统
函数说明:
/**
* 挂载SPIFFS文件系统
* @return bool 如果文件系统挂载成功,返回true,否则返回false
*/
bool begin();
注意点:
- 它必须在其他任何FS API被调用之前先调用;
- Arduino IDE配置时需要启用SPIFFS;
4.1.2 format —— 格式化文件系统
函数说明:
/**
* 格式化文件系统
* @return bool 如果格式化成功则返回true
*/
bool format();
注意点:
- 可以在执行begin()之前或者之后调用
4.1.3 open —— 打开文件
函数说明:
/**
* 打开文件,某种模式下会创建文件
* @param path 文件路径
* @param mode 存取模式
* @return File 返回一个File对象
*/
File open(const char* path, const char* mode);
File open(const String& path, const char* mode);
注意点:
- 路径必须是以斜线开头的绝对路径(如:/dir/filename.txt);
- 模式参数是个用字符串指定的存取模式,其值为“r”、“w”、“a”、“r+”、“w+”和“a+”之中的一个。
- r 以只读方式操作文件,读位置在文件的开始位置,文件不存在返回空对象;
- r+ 以可读可写方式打开文件,读写位置在文件的开始位置,文件不存在返回空对象;
- w 截取文件长度到0或者创建新文件,只能写操作,写位置在文件的开始位置;
- w+ 截取文件长度到0或者创建新文件,可读可写操作,写位置在文件的开始位置;
- a 在文件末尾追加内容或者文件不存在就创建新文件,追加位置在当前文件的末尾,只能写操作;
- a+ 在文件末尾追加内容或者文件不存在就创建新文件,追加位置在当前文件的末尾,可读写操作;
如果要检查文件是否打开成功,请使用以下代码:
File f = SPIFFS.open("/f.txt", "w");
if (!f) {
Serial.println("file open failed");
}
4.1.4 exists —— 路径是否存在
函数说明:
/**
* 路径是否存在
* @param path 文件路径
* @return bool 如果指定的路径存在,则返回true,否则返回false
*/
bool exists(const char* path);
bool exists(const String& path);
4.1.5 openDir —— 打开绝对路径文件夹
函数说明:
/**
* 打开绝对路径文件夹
* @param path 文件路径
* @return Dir 打开绝对路径文件夹,返回一个Dir对象
*/
Dir openDir(const char* path);
Dir openDir(const String& path);
4.1.6 remove —— 删除绝对路径的文件
函数说明:
/**
* 删除绝对路径的文件
* @param path 文件路径
* @return bool 如果删除成功则返回true,否则返回false
*/
bool remove(const char* path);
bool remove(const String& path);
4.1.7 rename —— 重新命名文件
函数说明:
/**
* 重新命名文件
* @param pathFrom 原始路径文件名
* @param pathTo 新路径文件名
* @return bool 如果重新命名成功则返回true,否则返回fals
*/
bool rename(const char* pathFrom, const char* pathTo);
bool rename(const String& pathFrom, const String& pathTo);
4.1.8 info —— 获取文件系统的信息
函数说明:
/**
* 获取文件系统的信息,存储在FSInfo对象
* @param info FSInfo对象
* @return bool 是否获取成功
*/
bool info(FSInfo& info);
FSInfo定义如下:
struct FSInfo {
size_t totalBytes;//整个文件系统的大小
size_t usedBytes;//文件系统所有文件占用的大小
size_t blockSize;//SPIFFS块大小
size_t pageSize;//SPIFFS逻辑页数大小
size_t maxOpenFiles;//能够同时打开的文件最大个数
size_t maxPathLength;//文件名最大长度(包括一个字节的字符串结束符)
};
4.2 Dir对象专用方法
在上面的方法中,我们可以获取到Dir对象,那么看看Dir对象定义是什么?
class Dir {
public:
Dir(DirImplPtr impl = DirImplPtr()): _impl(impl) { }
File openFile(const char* mode);//打开文件
String fileName();//获取文件名字
size_t fileSize();//文件大小
bool next();//下一个文件
protected:
DirImplPtr _impl;
};
注意点:
- Dir对象的作用主要是遍历文件夹里的所有文件;
- 文件夹并不是真正意义上的文件夹,文件都是平铺的;
4.2.1 openFile —— 打开文件
函数说明:
/**
* 打开文件
* @param mode 打开模式,请参考open方法
* @return File 返回一个File对象
*/
File openFile(const char* mode);
4.2.2 fileName —— 获取文件名字
函数说明:
/**
* 获取文件大小
* @return size_t 文件大小
*/
size_t fileSize();
4.2.3 next —— 是否还有下一个文件
函数说明:
/**
* 是否还有下一个文件
* @return bool true 表示还有文件
*/
bool next();
注意点:
- 其实这里用到了遍历;
- 只要还有文件,dir.next()就会返回true,这个方法必须在fileName()和openFile()方法之前调用。
4.3 File对象专用方法
那么,我们来看看File对象结构:
class File : public Stream
{
public:
File(FileImplPtr p = FileImplPtr()) : _p(p) {}
// Print methods:
size_t write(uint8_t) override;
size_t write(const uint8_t *buf, size_t size) override;
// Stream methods:
int available() override;
int read() override;
int peek() override;
void flush() override;
size_t readBytes(char *buffer, size_t length) override {
return read((uint8_t*)buffer, length);
}
size_t read(uint8_t* buf, size_t size);
bool seek(uint32_t pos, SeekMode mode);
bool seek(uint32_t pos) {
return seek(pos, SeekSet);
}
size_t position() const;
size_t size() const;
void close();
operator bool() const;
const char* name() const;
protected:
FileImplPtr _p;
};
File对象支持Stream的所有方法,因此可以使用readBytes、findUntil、parseInt、printIn以及其他stream方法。以下是File对象特有的一些方法:
4.3.1 seek —— 文件偏移位置
函数说明:
/**
* 设置文件位置偏移
* @param pos 偏移量
* @param mode 偏移模式
* @return bool 如果移动成功,则返回true,否则返回false
*/
bool seek(uint32_t pos, SeekMode mode);
bool seek(uint32_t pos) {
return seek(pos, SeekSet);
}
注意点:
- 如果模式值是 SeekSet,则从文件开头移动指定的偏移量。
- 如果模式值是 SeekCur,则从目前的文件位置移动指定的偏移量。
- 如果模式值是 SeekEnd,则从文件结尾处移动指定的偏移量。
4.3.2 position —— 返回目前在文件中的位置
函数说明:
/**
* 返回目前在文件中的位置
* @return size_t 当前位置
*/
size_t position();
4.3.3 size —— 返回文件大小
函数说明:
/**
* 返回文件大小
* @return size_t 文件大小
*/
size_t size();
4.3.4 name —— 返回文件名字
函数说明:
/**
* 返回文件名字
* @return const char* 文件名字
*/
const char* name();
4.3.5 close —— 关闭文件
函数说明:
/**
* 关闭文件
*/
void close();
注意点:
- 执行这个方法之后,就不能在该文件上执行其他操作。
5. 实例
5.1 文件操作
实例说明:
spiffs文件操作常见方法使用,包括文件查找、创建、打开、关闭、删除
实例源码:
/**
* 功能描述:spiffs文件操作常见方法使用,包括文件查找、创建、打开、关闭、删除
*/
#include <FS.h>
//以下三个定义为调试定义
#define DebugBegin(baud_rate) Serial.begin(baud_rate)
#define DebugPrintln(message) Serial.println(message)
#define DebugPrint(message) Serial.print(message)
#define myFileName "mydemo.txt"
void setup(){
DebugBegin(9600);
DebugPrintln("Check Start SPIFFS...");
//启动SPIFFS,如果下载配置没有配置SPIFFS,返回false
if(!SPIFFS.begin()){
DebugPrintln("Start SPIFFS Failed!please check Arduino Download Config.");
return;
}
DebugPrintln("Start SPIFFS Done.");
//判断文件是否存在
if(SPIFFS.exists(myFileName)){
DebugPrintln("mydemo.txt exists.");
}else{
DebugPrintln("mydemo.txt not exists.");
}
File myFile;
//打开文件 不存在就创建一个 可读可写
myFile = SPIFFS.open(myFileName,"w+");
//关闭文件
myFile.close();
//再次判断文件是否存在
if(SPIFFS.exists(myFileName)){
DebugPrintln("mydemo.txt exists.");
}else{
DebugPrintln("mydemo.txt not exists.");
}
//删除文件
DebugPrintln("mydemo.txt removing...");
SPIFFS.remove(myFileName);
//再次判断文件是否存在
if(SPIFFS.exists(myFileName)){
DebugPrintln("mydemo.txt exists.");
}else{
DebugPrintln("mydemo.txt not exists.");
}
}
void loop(){
}
实验结果:
5.2 文件列表
实例说明:
查看spiffs文件系统列表
实例准备:
- NodeMcu开发板
- 烧录配置需要开启SPIFFS
实例源码:
/**
* 功能描述:查看spiffs文件系统列表
*/
#include <FS.h>
//以下三个定义为调试定义
#define DebugBegin(baud_rate) Serial.begin(baud_rate)
#define DebugPrintln(message) Serial.println(message)
#define DebugPrint(message) Serial.print(message)
void setup(){
DebugBegin(9600);
DebugPrintln("Check Start SPIFFS...");
//启动SPIFFS,如果下载配置没有配置SPIFFS,返回false
if(!SPIFFS.begin()){
DebugPrintln("Start SPIFFS Failed!please check Arduino Download Config.");
return;
}
DebugPrintln("Start SPIFFS Done.");
File myFile;
//打开文件 不存在就创建一个 可读可写
myFile = SPIFFS.open("/myDemo.txt","w+");
//关闭文件
myFile.close();
//打开文件 不存在就创建一个 可读可写
myFile = SPIFFS.open("/myDemo.jpg","w+");
//关闭文件
myFile.close();
//打开文件 不存在就创建一个 可读可写
myFile = SPIFFS.open("/myDemo.html","w+");
//关闭文件
myFile.close();
Dir dir = SPIFFS.openDir("/");
while(dir.next()){
String fileName = dir.fileName();
size_t fileSize = dir.fileSize();
Serial.printf("FS File:%s,size:%d\n",fileName.c_str(),fileSize);
}
DebugPrintln("Setup Done!");
}
void loop(){
}
实验结果:
5.3 文件读写
实例说明:
往文件myDemo.txt中写入“单片机菜鸟博哥666”并读取出来显示。
实例源码:
/**
* 功能描述:演示文件读写功能
*/
#include <FS.h>
//以下三个定义为调试定义
#define DebugBegin(baud_rate) Serial.begin(baud_rate)
#define DebugPrintln(message) Serial.println(message)
#define DebugPrint(message) Serial.print(message)
void setup(){
DebugBegin(9600);
DebugPrintln("Check Start SPIFFS...");
//启动SPIFFS,如果下载配置没有配置SPIFFS,返回false
if(!SPIFFS.begin()){
DebugPrintln("Start SPIFFS Failed!please check Arduino Download Config.");
return;
}
DebugPrintln("Start SPIFFS Done.");
File myFile;
//打开文件 不存在就创建一个 可读可写
myFile = SPIFFS.open("myDemo.txt","w+");
if(myFile){
DebugPrintln("Writing something to myDemo.txt...");
myFile.println("单片机菜鸟博哥666");
myFile.close();
DebugPrintln("Writing Done.");
}else{
DebugPrintln("Open File Failed.");
}
//打开文件 可读
myFile = SPIFFS.open("myDemo.txt","r");
if(myFile){
DebugPrintln("Reading myDemo.txt...");
while(myFile.available()){
//读取文件输出
Serial.write(myFile.read());
}
myFile.close();
}else{
DebugPrintln("Open File Failed.");
}
DebugPrintln("Setup Done!");
}
void loop(){
}
实验结果:
5.4 烧写文件
实验说明:
在上面的例子中,我们都是自己手动在SPIFFS文件系统中创建或者写入文件,但是对于习惯web开发的人员来说,肯定是直接把写好的web程序(html、css、js、资源文件等)直接烧入文件系统更加令人容易接受。所以本例子主要是讲解如何往SPIFFS里面烧写文件。
这个例子是重点,因为绝大部分的web开发(web配网、web页面等)都是常用烧写文件的方式,请读者仔细阅读。
要存入SPIFFS区域的文件,都得事先放在代码目录里的“data”目录(请自行新增“data”目录)。
例如,存在一个项目工程叫做espStaticWeb,其文件结构如下:
负责将文件上传到SPIFFS的工具叫做 ESP8266FS。ESP8266FS是一个集成到Arduino IDE中的工具,它将一个菜单项添加到工具菜单,用于将skench data目录的内容上传到ESP8266 Flash文件系统中。
这个工具需要另外安装,整个上传文件步骤如下:
- 下载 ESP8266FS工具
- 将下载到的文件解压到Arduino IDE安装路径下的tools文件夹(如果不存在这个文件夹,请自行增加)。参考下图:
- 重启Arduino IDE
- 打开一个Sketch工程(新建或者打开最近的工程),去到Sketch工程目录下创建一个data目录(不存在该目录),然后把你需要放到文件系统的文件copy到这里。
- 确保你选择了正确的板子、com口,关闭掉串口监视器。
- 选择 工具 ESP8266 Sketch Data Upload
然后就会开始上传文件到ESP8266 flash文件系统。
当IDE显示“SPIFFS Image Uploaded”,代表上传完毕。
那么接下来说明一下本例子内容:
- 往8266 SPIFFS文件系统中上传一个config.txt文件(请读者自行创建,然后放在data目录,上传到ESP8266),然后读取出来。文件内容包括:
{"name":"esp8266","flash":"QIO","board":"NodeMcu"}
实验准备:
- 往8266 SPIFFS文件系统中上传一个config.txt文件(请读者自行创建,然后放在data目录,上传到ESP8266)
- NodeMcu开发板
实验源码:
/**
* 功能描述:演示上传文件并读取文件内容
* 前提:需要先往SPIFFS里面上传config.txt文件
*/
#include <FS.h>
//以下三个定义为调试定义
#define DebugBegin(baud_rate) Serial.begin(baud_rate)
#define DebugPrintln(message) Serial.println(message)
#define DebugPrint(message) Serial.print(message)
void setup(){
DebugBegin(9600);
DebugPrintln("Check Start SPIFFS...");
//启动SPIFFS,如果下载配置没有配置SPIFFS,返回false
if(!SPIFFS.begin()){
DebugPrintln("Start SPIFFS Failed!please check Arduino Download Config.");
return;
}
DebugPrintln("Start SPIFFS Done.");
File myFile;
//打开文件 不存在就创建一个 可读可写
myFile = SPIFFS.open("/config.txt","r");
if(myFile){
//打印文件大小
int size = myFile.size();
Serial.printf("Size=%d\r\n", size);
//读取文件内容
DebugPrintln(myFile.readString());
myFile.close();
DebugPrintln("Reading Done.");
}else{
DebugPrintln("Open File Failed.");
}
}
void loop(){
}
实验结果:
6. 总结
SPIFFS文件系统属于非常重要的一篇,希望读者可以认真理解使用。
ESP8266开发之旅 网络篇⑬ SPIFFS——ESP8266 SPIFFS文件系统的更多相关文章
- ESP8266开发之旅 网络篇⑯ 无线更新——OTA固件更新
授人以鱼不如授人以渔,目的不是为了教会你具体项目开发,而是学会学习的能力.希望大家分享给你周边需要的朋友或者同学,说不定大神成长之路有博哥的奠基石... QQ技术互动交流群:ESP8266&3 ...
- ESP8266开发之旅 网络篇⑦ TCP Server & TCP Client
授人以鱼不如授人以渔,目的不是为了教会你具体项目开发,而是学会学习的能力.希望大家分享给你周边需要的朋友或者同学,说不定大神成长之路有博哥的奠基石... QQ技术互动交流群:ESP8266&3 ...
- ESP8266开发之旅 网络篇⑧ SmartConfig——一键配网
授人以鱼不如授人以渔,目的不是为了教会你具体项目开发,而是学会学习的能力.希望大家分享给你周边需要的朋友或者同学,说不定大神成长之路有博哥的奠基石... QQ技术互动交流群:ESP8266&3 ...
- ESP8266开发之旅 网络篇⑨ HttpClient——ESP8266HTTPClient库的使用
授人以鱼不如授人以渔,目的不是为了教会你具体项目开发,而是学会学习的能力.希望大家分享给你周边需要的朋友或者同学,说不定大神成长之路有博哥的奠基石... QQ技术互动交流群:ESP8266&3 ...
- ESP8266开发之旅 网络篇⑩ UDP服务
授人以鱼不如授人以渔,目的不是为了教会你具体项目开发,而是学会学习的能力.希望大家分享给你周边需要的朋友或者同学,说不定大神成长之路有博哥的奠基石... QQ技术互动交流群:ESP8266&3 ...
- ESP8266开发之旅 网络篇⑪ WebServer——ESP8266WebServer库的使用
授人以鱼不如授人以渔,目的不是为了教会你具体项目开发,而是学会学习的能力.希望大家分享给你周边需要的朋友或者同学,说不定大神成长之路有博哥的奠基石... QQ技术互动交流群:ESP8266&3 ...
- ESP8266开发之旅 网络篇⑭ web配网
1. 前言 目前,市面上流行多种配网方式: WIFI模块的智能配网(SmartConfig以及微信AirKiss配网) SmartConfig 配网方式 请参考博主之前的博文 ESP8266开 ...
- ESP8266开发之旅 网络篇③ Soft-AP——ESP8266WiFiAP库的使用
1. 前言 在前面的篇章中,博主给大家讲解了ESP8266的软硬件配置以及基本功能使用,目的就是想让大家有个初步认识.并且,博主一直重点强调 ESP8266 WiFi模块有三种工作模式: St ...
- ESP8266开发之旅 网络篇④ Station——ESP8266WiFiSTA库的使用
1. 前言 在前面的篇章中,博主给大家讲解了ESP8266的软硬件配置以及基本功能使用,目的就是想让大家有个初步认识.并且,博主一直重点强调 ESP8266 WiFi模块有三种工作模式: St ...
随机推荐
- Java门面模式
一.简介 隐藏系统的复杂性,对外提供统一的访问入口,外部系统访问只通过此暴露出的统一接口访问.是一种结构型模式.封装子系统接口的复杂性,提供统一的对外接口,能够使子系统更加简单的被使用. 二.结构及使 ...
- svn新建文件不能提交的解决方法
svn新建文件不能提交的解决方法 在当前新建文件的目录下,右键空白处: 选择Properties 找到所有有ignore字眼的属性,查看这个属性的继承目录(inherited from),入我的是cl ...
- Git使用教程-idea系列中git使用教程
一.新建项目 新建项目后记得复制git仓库的地址. 二.上传项目到git仓库 在你的idea里新建git仓库,这是新建本地仓库,等会会同步到线上git仓库 新建后如果代码不是文件名不是绿色 ...
- 如何判断前后端bug
测试工程师不只是负责发现问题,除了发现问题这种基本功外,定位问题,提出解决方案,提出预防方案也是要掌握的技能.这里先说定位问题的要求,定位问题要向深入,前提当然是对功能.产品的流程.开发方案.开发人员 ...
- Maven项目运行报错提示找不到加载主类
遇到这个问题花了几小时时间看网上的各种解决方法.试了几种都没用,最后用了这种本办法. 亲测第三条 https://blog.csdn.net/yuliantao/article/details/766 ...
- java8 Stream使用总结
[前言] java8新特性 java8 函数接口 java8 Optional使用总结 Java 8 时间日期使用 java8 lambda表达式 1.流的介绍 Java8 中的 Stream 是对集 ...
- node环境及vscode搭建
软件下载: nodejs https://nodejs.org/en/ vscode https://code.visualstudio.com/docs/?dv=win python-2.7.15 ...
- v-text和v-html的区别
一.v-text 用于渲染普通文本,无论何时,绑定的数据对象上 msg属性发生了改变,插值处的内容都会更新. <span v-text="message"></s ...
- CSS技巧 (2) · 多列等高布局
前言 最近,面试的时候都碰到一些关于利用CSS实现多列等高布局或者一侧宽度固定,另一侧宽度自适应的问题,下面稍微总结一下: 先看一道题目 巧妙的多列等高布局 规定下面的布局,实现多列等高布局,要求两 ...
- Redis未授权访问漏洞复现
Redis未授权访问漏洞复现 一.漏洞描述 Redis默认情况下,会绑定在0.0.0.0:6379(在redis3.2之后,redis增加了protected-mode,在这个模式下,非绑定IP或者没 ...