目的:
ESP32 の ファイルシステム (LittleFS) にあるファイルをダウンロードする 。概要:
LittleFSにファイルが格納された状態でダウンロード用スケッチを書き込み、WEB経由でファイルをPC等にダウンロードする。制限事項として、
ファイルはカレントディレクトリのみの対応。
ファイルの最大数は 50 固定
としている。
WEB画面サンプル
スケッチコード:
以下にスケッチのコードを示す。// +==========================================================================+
// | LittleFS data download |
// +==========================================================================+
#include <WiFi.h> //
#include <WebServer.h> // WebServer 使用の為
#include <ArduinoJson.h> // JSOM 使用 の為
#include "FS.h" // LittleFS 使用に必要
#include "LittleFS.h" // LittleFS 使用に必要
// //
// +--------------------------------------------------------------------------+
// | WiFi 設定 他 |
// +--------------------------------------------------------------------------+
const char* ssid = "XXXXXXXXXXXXXXX"; // WiFi AP SSID
const char* password = "xxxxxxxxxxxxx"; // WiFi AP パスワード
// //
IPAddress ip(192, 168, 1, 32); // ESP32 IPアドレス
IPAddress gateway(192,168, 1, 1); // ゲートウェイアドレス
IPAddress subnet(255, 255, 255, 0); // サブネットマスク
IPAddress DNS(192, 168, 1, 91); // DNS アドレス
// //
WebServer server(80); // WebServer オブジェクト生成
// //
// +--------------------------------------------------------------------------+
// | グローバル変数 |
// +--------------------------------------------------------------------------+
String filelist[50] ; // ファイルリスト 最大 50
int fileCount = 0 ; // ファイル数 カウント用
// //
// +--------------------------------------------------------------------------+
// | Main HTML , JAVA Script |
// +--------------------------------------------------------------------------+
const char* htmlPage = R"rawliteral(
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style>
.filelist{width:400px;}
input {margin:4px;width:100px;}
div {font-size:16pt;text-align:center;width:350px;}
</style>
<title>LittleFS ファイルリスト</title>
</head>
<body>
<h3>LittleFS dataリスト</h3>
<div>
<form method='get'>
<input type='button' name='btn1' value='更新' onclick='loadList()' >
<input type='button' name='btn2' value='ダウンロード' onclick='getSelVal()'>
<input type='button' name='btn3' value='削除' onclick='removeFile()'>
</form>
</div>
<select class="filelist" id="listBox" size="5" ></select>
<script>
// ファイルリストを取得 ---------------------
async function loadList() { // ファイルリスト取得
const response = await fetch("/list"); // fetch で リストをjsonで 取得
const data = await response.json(); // json の解釈
const listBox = document.getElementById("listBox"); // listBox オブジェクト取得
listBox.innerHTML = ""; // ListBox を一度クリア
data.filelist.forEach(item => { // ListBox にファイルリストを 格納
const option = document.createElement("option");
option.text = item;
listBox.add(option);
});
}
// ファイルを取得 ---------------------------
async function getSelVal() { // 選択したリストをダウンロードする
// リストの選択した値からファイル名を取得する
const selectElement = document.getElementById("listBox"); // listBox の要素を取得
const selectedValue = selectElement.value ; // 要素の値を取得
if (selectedValue != "") { // 要素が選択されている場合のみ実行
const selectedVArry = selectedValue.split("\/"); // 値を "/" で分割
const selectedfname= selectedVArry[selectedVArry.length - 1]; // 分割した最後の値(ファイル名)を取得
// ファイル名の内容を要求し、ダウンロードする
try {
const response = await fetch("/data?file=" + selectedValue) ; // GET /data?file=<ファイル名> 発行
const bdt = await response.blob() ; // 応答を blob として受信して bdt に格納
const url = window.URL.createObjectURL(bdt) ; // blobのダウンロード先URLを生成
const a = document.createElement('a') ; // a タグを生成
a.download = selectedfname ; // download属性にファイル名を設定
a.href = url ; // href属性に URLを設定
a.click() ; // a タグをクリックしてダウンロード実行
window.URL.revokeObjectURL(url) ; // url オブジェクトの開放
} catch (error) { // ファイル取得できなかったときの処理
alert("file read error") ; //
}
}
}
// ファイルを削除 ---------------------------
async function removeFile() { //
// リストの選択した値からファイル名を取得して、削除要求を発行する
const selectElement = document.getElementById("listBox"); // listBox の要素を取得
const selectedValue = selectElement.value ; // 要素の値を取得
if (selectedValue != "") { // 要素が選択されている場合のみ実行
const response = await fetch("/del?file=" + selectedValue) ; // GET /del?file=<ファイル名> 発行
loadList() ; // ファイルリスト取得
} //
}
// 最初にファイルリストを取得する //
loadList() ; //
</script>
</body>
</html>
)rawliteral";
// //
// +--------------------------------------------------------------------------+
// | ハンドル処理 : WEBからの要求に対する応答処理 |
// +--------------------------------------------------------------------------+
void handleRoot() { //メイン HTMLページを返す
get_client_msg() ; // *- デバッグ用
if (server.hasArg("btn2")) { // | 受信メッセージを表示
Serial.println(" hit btn2 ") ; // |
} // +---------------
server.send(200, "text/html", htmlPage); // HTMLを送信
} //
// ファイルのリストを作成して送信 //
void handleList() { //ファイルリスト要求の応答
DynamicJsonDocument doc(512); // JSON データ用変数
JsonArray arr = doc.createNestedArray("filelist");// JSON配列を作成(キー:filelist)
Serial.println("Client access /list ") ; // デバッグ用表示
listDir("/") ; // ファイルリスト取得
for (int i = 0; i < fileCount; i++) { // ファイルリストを
arr.add(filelist[i]); // JSON配列に追加
} //
String json; // JSON 文字列変数
serializeJson(doc, json); // JSONを文字列に変換
Serial.println("json : ") ; // +- デバッグ用表示
Serial.println(json) ; // +----------------
server.send(200, "application/json", json); // JSONデータを送信
} //
// 選択されたファイルのデータを送信 //
void handleFileData() { // ファイル送信要求の応答
String sdt = "Client access /data \n" ; // +- デバッグ用表示
sdt += get_client_msg() ; // |
Serial.println(sdt) ; // +----------------
String fname = server.arg("file") ; // file の値(ファイル名)取得
Serial.println("fname : " + fname ) ; // +- デバッグ用表示
File fp = LittleFS.open(fname,"r") ; // ファイルをオープンする
if (!fp) { // オープン失敗の場合
server.send(404, "text/plain", "File not found"); //
return ; //
} //
server.streamFile(fp, "application/octet-stream"); // ファイル送信
fp.close(); // ファイルをクローズ
} //
// 選択されたファイルのデータを削除 //
void handleFileDel() { //
String sdt = "Client access /del \n" ; // +- デバッグ用表示
sdt += get_client_msg() ; // |
Serial.println(sdt) ; // +----------------
String fname = server.arg("file") ; // file の値(ファイル名)取得
Serial.println("fname : " + fname ) ; // +- デバッグ用表示
String res = del_LittleFS(fname) ; // ファイルを削除
server.send(200, "text/plain",res) ; // 結果を送信
} //
// URLが不明な場合にメッセージを送信 //
void handleNotFound() { // 不明なURL受診時の処理
String message = "File Not Found\n\n"; // 接続先のURLが 無い場合の
message += get_client_msg() ; // メッセージ作成
server.send(404, "text/plain", message); // メッセージを WEB に送信
} //
// 受信した要求の内容を文字列に格納して返す //
String get_client_msg() { // 受信した要求の内容を作成
String s_req = "request param ----\n"; // Clientからの要求
s_req += "URI: " + server.uri() + "\n" ; //
s_req += "Method: "; //
s_req += (server.method() == HTTP_GET) ? "GET\n" : "POST\n"; //
s_req += "Arguments: " + String(server.args()) + "\n" ; //
for (uint8_t i = 0; i < server.args(); i++) { //
s_req += " " + server.argName(i) + ": " ; //
s_req += server.arg(i) + "\n" ; //
} //
s_req += "------------------ \n"; //
Serial.print(s_req); // シリアルに表示
return s_req ; // 呼び出し元に文字列を応答
} //
// *--------------------------------------------------------------------------+
// | 処理関数 : arduino (ESP32) 内部処理用関数 |
// +--------------------------------------------------------------------------+
// //
void listDir( const char *dirname) { // ファイルリストの取得
Serial.printf("Listing directory: %s\r\n", dirname); //
File root = LittleFS.open(dirname); // 指定ディレクトをオープン
if (!root) { //
Serial.println("- failed to open directory");//
return; //
} //
if (!root.isDirectory()) { // ディレクトリか判定
Serial.println(" - not a directory"); // ディレクトリでなければ
return; // 戻る
} //
// //
fileCount = 0 ; // ファイル数カウント 初期化
File file = root.openNextFile(); // 最初のファイルをオープンする
while (file) { // ファイルがある限りループ
if (file.isDirectory()) { // ディレクトリか判定
Serial.print(" DIR : "); // シリアルに情報表示
Serial.println(file.name()); //
//if (levels) { // 指定階層まで再帰的に
// listDir(file.path()) ; // リストを取得
//} // * 非対応
} else { // ファイルの場合の処理
Serial.print(" FILE: "); // +--シリアルに情報表示
Serial.print(fileCount); // |
Serial.print(" "); // |
//Serial.print(file.name()); // |
Serial.print(file.path()); // |
Serial.print("\tSIZE: "); // |
Serial.println(file.size()); // +--------------------
//filelist[fileCount] = String(file.path()) + file.name() ; // リストにファイル名を追加
filelist[fileCount] = file.path(); // リストにファイル名を追加
fileCount ++ ; // ファイル数をインクリメント
if (fileCount <50) { //
file = root.openNextFile(); // 次のファイルをオープン
} else { //
break ; //
} //
} //
} //
} //
// //
String del_LittleFS(String fname) { // LittleFS のファイル削除
String result ; //
Serial.println(" ------ Check_file ----------") ; //
if (LittleFS.exists(fname)) { // fname ファイル存在確認
Serial.println(fname + " 有り") ; // あった場合の表示
} else { //
Serial.println(fname + " なし") ; // 無かった場合の表示
} //
if (LittleFS.remove(fname)) { // fname ファイル削除
Serial.println(fname + " 削除成功") ; // 成功時の表示
result = "success" ; //
} else { //
Serial.println(fname + " 削除失敗") ; // 失敗時の表示
result = "failure" ; //
} //
return result ; //
} //
// *--------------------------------------------------------------------------+
// | 初期設定 |
// +--------------------------------------------------------------------------+
void setup() //
{ //
Serial.begin(115200); // シリアル出力開始
WiFi.config(ip, gateway, subnet, DNS); // ESP32 WiFi 設定
delay(10); //
Serial.println(); // デバッグ用出力
Serial.print("Connecting to "); // :
Serial.println(ssid); // :
WiFi.begin(ssid, password); // WiFi 開始
while (WiFi.status() != WL_CONNECTED) { // WiFi 接続待ち
delay(500); //
Serial.print("."); //
} //
Serial.println(""); // デバッグ用出力
Serial.println("WiFi connected."); // 接続先の IP address を
Serial.println("IP address: "); // シリアルに出力
Serial.println(WiFi.localIP()); //
// //
// ハンドル処理の登録 //
server.on("/", HTTP_GET, handleRoot); // メインHTML の設定
server.on("/list", handleList); // ファイルリスト要求の応答
server.on("/data", handleFileData) ; // ファイル送信要求の応答
server.on("/del", handleFileDel) ; // ファイル削除要求の応答
server.onNotFound(handleNotFound) ; // 存在しない URL の場合
// //
// WEBサーバー開始 //
server.begin(); // WiFi サーバ 開始
if (!LittleFS.begin(true)) { // LittleFS 開始
Serial.println("LittleFS のマウントに失敗") ; //
return ; //
} //
} //
// //
// +--------------------------------------------------------------------------+
// | メインループ |
// +--------------------------------------------------------------------------+
void loop() { //
// クライアント処理 //
server.handleClient(); //
// //
} //
// //
処理概要:
処理の概要は以下の通り。- WEB画面
「更新」,「ダウンロード」,「削除」 のボタンを持ち、それぞれクリックされたら javascript を呼び出す。 また、リストボックスを設置する。 - JavaScript
各ボタンに対応する function を持つ。
loadList : 更新時、ファイルリストを ESP32 へ要求し、結果(json) をリストボックスに表示する。
getSelVal : ダウンロード時、選択されているファイル名のファイルを ESP32 へ要求し、結果(BSOB)をPC にダウンロードする。
removeFile : 削除時、選択されているファイル名のファイルの削除を ESP32 へ要求し、終了後、LoadList を実行する。
- ハンドル処理
WEBクライアントからの各要求に対する応答処理を行う。
handlRoot() : main HTML の送信を行う。
handleList() : ファイルリスト要求に対する応答。
LittleFS のファイルリストを作成し、JSON形式でWEBクライアントに送信する。
handleFileData() : ファイル要求に対する応答。
要求された LittleFS のファイルを server.streamFile でWEBクライアントに送信する。
handleFileDel() : 削除要求に対する応答。
要求された LittleFS のファイルを削除し、結果(成否 の文字列) をWEBクライアントに送信する。
handleNotFound() : 不明なURL受診時の処理。
クライアントからの受信内容をWEBクライアントに 404 で送信する。
- 内部処理
listDir() : LittleFS のファイルリストを作成する。
del_LittleFS() : LittleFS から指定したファイルを削除する。
その他、詳細は スケッチコード内のコメントを参照。

0 件のコメント:
コメントを投稿