質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Webサーバー

Webサーバーとは、HTTPリクエストに応じて、クライアントに情報を提供するシステムです。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Arduino

Arduinoは、AVRマイコン、単純なI/O(入出力)ポートを備えた基板、C言語を元としたArduinoのプログラム言語と、それを実装した統合開発環境から構成されたシステムです。

HTML

HTMLとは、ウェブ上の文書を記述・作成するためのマークアップ言語のことです。文章の中に記述することで、文書の論理構造などを設定することができます。ハイパーリンクを設定できるハイパーテキストであり、画像・リスト・表などのデータファイルをリンクする情報に結びつけて情報を整理します。現在あるネットワーク上のほとんどのウェブページはHTMLで作成されています。

Q&A

解決済

2回答

4550閲覧

[ESP32][ASyncwebserver]ESP32側で動的に生成されるSPIFFS内のログファイルを、クライアント側にダウンロードしたい

Mudo_Ayumu

総合スコア7

Webサーバー

Webサーバーとは、HTTPリクエストに応じて、クライアントに情報を提供するシステムです。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Arduino

Arduinoは、AVRマイコン、単純なI/O(入出力)ポートを備えた基板、C言語を元としたArduinoのプログラム言語と、それを実装した統合開発環境から構成されたシステムです。

HTML

HTMLとは、ウェブ上の文書を記述・作成するためのマークアップ言語のことです。文章の中に記述することで、文書の論理構造などを設定することができます。ハイパーリンクを設定できるハイパーテキストであり、画像・リスト・表などのデータファイルをリンクする情報に結びつけて情報を整理します。現在あるネットワーク上のほとんどのウェブページはHTMLで作成されています。

0グッド

0クリップ

投稿2021/10/08 06:34

編集2021/10/08 06:54

概要

ESP32にsoftAP機能とAsyncwebserverを実装して、非同期Webサーバとして運用しています
ESP32はSPIFFSファイルシステムを利用しており、HTMLなどの静的ファイルやログcsvなどの動的ファイルが保存されています
イメージ説明

実現したいこと

クライアント側 Web画面上からの操作でSPIFFS内のログcsvをダウンロード(PC側に保存)したいです
ただ、ログcsvは動的に生成されるためあらかじめファイル名が決まっておらず、server.onでファイル名にアクセスされた時の動作を規定するAsyncwebserverの一番簡単な方法が使えずに困っています

問題点

オーソドックスにSPIFFS内に保存されたファイルをWebページ上からダウンロードしようと思ったら、下記の様になりますよね

HTML

1<a href="/log/test.csv">dammy.csv</a>

Arduino

1server.on("/log/test.csv", HTTP_GET, [](AsyncWebServerRequest *request){ 2 request->send(SPIFFS, "/log/test.csv", "text/csv"); 3 delay(1) ; 4 });

しかし、上記例では試験的にあらかじめ用意したtest.csvという名前のファイルをダウンロードするだけだからいいですが、実際にはログは動的に生成され、ファイル名が都度変わります
ファイル名が事前に決まっていないとserver.onの待ち受けアドレス(最初の引数)でファイル名にアクセスされるのを待ち受ける形にはできません

試したこと

a hrefを使うのではなく、クライアント側からダウンロードしたいファイルのパスをFormDataに含んだPOSTを発出し、POSTを受けてサーバ側が関数を呼び出し、該当のパスのファイルをレスポンスとしてクライアント側に返すようにしてみましたが、何の反応もありません。
レスポンスがうまくできていないのか、もしくはレスポンス自体は帰っているけど、クライアント側が「これはダウンロードするデータだ」と認識していなくてスルーしているのか分かりませんが…javascript側にレスポンスを待ち受ける記述が必要なのでしょうか…

HTML

1<input id="path_input" type="text" name="path_input"><br> 2<button id="path" type="button" name="path" form="path_input" value="form_value" onclick="POST_event(this);">DOWNLOAD</button>

JavaScript

1function POST_event(ele) 2 { 3 var formID = ele.getAttribute("form"); 4 var buttonNAME = ele.getAttribute("name"); 5 var buttonVALUE = document.getElementById(formID).value; 6 7 var XHR = new XMLHttpRequest(); 8 var postDatas = new FormData(); 9 postDatas.append(buttonNAME, buttonVALUE); 10 XHR.open("POST", "/POST_Receiver", true); 11 XHR.send(postDatas); 12 }

Arduino

1// POST解析&処理 2 server.on("/POST_Receiver", HTTP_POST, [](AsyncWebServerRequest *request) { 3 int params = request->params(); 4 for(int i=0;i<params;i++) 5 { 6 AsyncWebParameter* p = request->getParam(i); 7 String name = p->name().c_str(); 8 String value = p->value().c_str(); 9 if( name == "path" ){AsyncWebServerResponse *response = request->beginResponse(SPIFFS, "/test.csv", "text/csv"/*value.c_str(), String(), true*/); request->send(response);} 10 } 11 delay(1) ; 12 });

要約

実現したいことは、動的に生成される、あらかじめファイル名が定まっていないログファイルを、クライアント側にダウンロードしたい、ということです
「試したこと」の方向性自体が間違っているのか、それとも方向性は合っているけど記述が不足している、間違っているのか
他にこのような方法があるんじゃないのか、などアドバイスいただけると嬉しいです

追記

今、一つパッと思いついたのですが、server.onNotFoundで指定されたパスのファイルがSPIFFS内に存在するかlistDirで検索して、一致した場合にそのファイルをrequest->sendすれば、a herfやJavaScriptのlocation.replace(link);でダウンロードできるかも…

それと、参考にしている(というか本家の)ページにこんな記述がありました。
https://github.com/me-no-dev/ESPAsyncWebServer#file-upload-handling

Arduino

1// save callback for particular URL path 2auto handler = server.on("/some/path", [](AsyncWebServerRequest *request){ 3 //do something useful 4});

これを使って、/log以下にアクセスされたときに起動するハンドラーが作れたりしないかなぁ…
本日は他の作業をやらなくてはならず時間がないため、検証は来週に行うことにします…

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

レスポンスがうまくできていないのか、もしくはレスポンス自体は帰っているけど、クライアント側が「これはダウンロードするデータだ」と認識していなくてスルーしているのか分かりませんが

原因を明確にする必要があると思います。例えば、"/POST_Receiver"の中で、paramsやname、valueが想定した値になっているのか、シリアルコンソールに表示して確認するとか。JavaScriptもですね。

あと、Webサーバでは同期通信しかしていないようなので、WebServerライブラリの枠組みを使って、以下のような方法もあると思います。

Arduino

1void handleReadFile() { 2 String path = server.arg("path"); 3 Serial.println("File name: " + path); // デバッグ用 4 5 SPIFFS.begin(true); 6 if (SPIFFS.exists(path)) { 7 File file = SPIFFS.open(path, "r"); 8 server.streamFile(file, "text/csv"); 9 file.close(); 10 } else { 11 server.send(200, "text/plain", path + " does not exist."); 12 } 13 SPIFFS.end(); 14}

このコードで、以下を実行すると、/test.csvの内容が返却されました。もちろん、server.on("/readFile", handleReadFile);して、/readFileで、readFile()が実行されるようにしています。

$ curl http://SERVERADDRESS/readFile?path=/test.csv

SPIFFSに関して、

  • SPIFFS.begin(true)は、SPIFFSが未フォーマットの場合はフォーマットするので注意してください。
  • SPIFFS.begin()は、setup()の中で実行しても問題ないと思います。その場合、SPIFFS.end()は、handleReadFile()の中で行わないでください。

投稿2021/10/09 23:17

編集2021/10/10 04:26
mkgt00

総合スコア165

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Mudo_Ayumu

2021/10/11 00:17

回答ありがとうございます。 現状、周りにアドバイスしてくれる人間がおらず独学でやっているので、迅速にアドバイスいただけて非常に助かります。 --- 本日、追検証の時間が取れないので後日もう一度確認してみますが、 一度返信させていただきます。 Asyncwebserverは普通のwebserverと同じメソッドが使えないことが多いようで、 server.arg、server.streamFileは一度試したことがありますが、これらも使えなかったと思います。 質問文の中では文字数制限対策に省略していましたが、/POST_Receiverで受け取ったname,valueは想定通りであることを確認しています。 Serial.printf("\n[POST Received]\t"); Serial.printf("name:%s\tvalue:%s\n",name.c_str(),value.c_str()); 以上、時間が取れた時にもう一度確認してみます
mkgt00

2021/10/11 08:00

> Asyncwebserverは普通のwebserverと同じメソッドが使えないことが多いようで、 server.arg、server.streamFileは一度試したことがありますが、これらも使えなかったと思います。 私が書いたのは、以下の通りで、WebServerライブラリを使う方法です。 > Webサーバでは同期通信しかしていないようなので、WebServerライブラリの枠組みを使って JavaScriptからではなく、curlなどのツールを使って、POSTの結果を取得してみるのが早いのではと思いました。うまく取得できていれば、JavaScript側の表示の問題だし、取得できていなければ、Webサーバ側の問題と、切り分けができるので。
Mudo_Ayumu

2021/10/11 09:28

この質問の内容は現在製作しているソフトの一部の機能のみに焦点を当てて質問しています。 質問の内容部分だけなら非同期である必要はないので通常のWebServerライブラリでもいいのですが、質問に書いてない部分で非同期である必要があるのでAsync~を使ってます。 curlというツールは知らなかったので少し調べてみましたが、すごく便利そうですね! 使い方など勉強してみようと思います。 サーバ側がPOSTを正しく受けてるところまで確認できているので、残りは ①そもそもサーバ側がレスポンスとしてファイルを投げていないのか ②サーバ側がレスポンスを返しているのにクライアント側が受け取れていないのか ここを切り分けるということですね
mkgt00

2021/10/11 11:39

AsyncWebServerを利用した、Arduino側のコードは正しく動作するようです(動作しました)。気になるのは、SPIFFS(begin)をしているか、本当に、/test.csvが存在するかくらいです。 あと、curlコマンドですが、今回は、POSTメソッドを使うので、以下が正しいです。前回のは、GETメソッドを使います。申し訳ありません。 curl -X POST "http://SERVERADDRESS/POST_Receiver" -d "path=/test.csv"
Mudo_Ayumu

2021/10/12 09:46

本日少しだけ時間が取れたので検証してみました。 curlからPOSTしてみた結果、レスポンスとしてtest.csvの中身の文字列が返っていることが確認できました。一歩前進です。 C:\curl\bin>curl -X POST "http://192.168.1.100/POST_Receiver" -d "path=" aaa,bbb,ccc ddd,eee,fff ということは、クライアント側がレスポンスを受け取っているが「ダウンロードするもの」として認識していないため、受け取っただけでスルーしているということでしょうか javascript側でレスポンスをダウンロードするような記述をしてあげればそれで解決できそうな気がしてきました
guest

0

function POST_event(ele)の中に下記の様に記述を入れることでレスポンスをダウンロードできました

javascript

1xhr.onreadystatechange = function() 2 { 3 if (this.readyState == 4 && this.status == 200) 4 { 5 let bom = new Uint8Array([0xEF, 0xBB, 0xBF]); 6 let blob = new Blob([bom, this.response], {type: 'text/csv'}); 7 var urlUtil = window.URL || window.webkitURL; 8 var DLUrl = urlUtil.createObjectURL(blob); 9 var link = document.createElement('a'); 10 link.href=DLUrl; 11 link.download = formVALUE; 12 document.body.appendChild(link); 13 link.click(); 14 } 15 };

let bom = new Uint8Array([0xEF, 0xBB, 0xBF]);
let blob = new Blob([bom, this.response], {type: 'text/csv'});
の2行はCSVにUTF-8 BOM情報を付与するものです。
BOMなしの場合は必要ありません。その場合は

javascript

1xhr.onreadystatechange = function() 2 { 3 if (this.readyState == 4 && this.status == 200) 4 { 5 var urlUtil = window.URL || window.webkitURL; 6 var DLUrl = urlUtil.createObjectURL(this.responce); 7 var link = document.createElement('a'); 8 link.href=DLUrl; 9 link.download = formVALUE; 10 document.body.appendChild(link); 11 link.click(); 12 } 13 };

でOKでした

投稿2021/10/13 09:51

Mudo_Ayumu

総合スコア7

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問