Synchronisieren von Dateiuploads zwischen Browserfenstern
Für PiCockpitIch entwickle derzeit die Digital Nose App.
Diese Anwendung erfordert einen Upload für die BSEC-Konfigurationsdatei, bei der es sich um binäre Daten handelt.
Sobald der Benutzer die Datei hochgeladen hat, wird sie über MQTT veröffentlicht, und der picockpit-Client kann sie zur Konfiguration des BSEC-KI-Algorithmus für die Gaserkennung verwenden.
Hier ist eine kleine Vorschau auf einen Teil des Webinterfaces:
Vuetify wird mit einem eingebauten GUI-Element geliefert, v-file-input:
<v-Dateieingabe
v-model="bsecFile"
label="BSEC-Konfigurationsdatei von AI Studio (.config)"
placeholder="Laden Sie Ihre BSEC-Konfigurationsdatei hier hoch"
prepend-icon="mdi-paperclip"
skizziert.
Dichtes
show-size
accept=".config"
@change="doUpload"
>
</v-file-input>
In diesem Fall, bsecFile ist mein Modell.
Hinweis: Da es sich um ein Modell handelt, kann es auch angeschrieben werden!
Veröffentlichung über MQTT
So sieht der Handler-Code doUpload aus (noch mit Debug-Informationen):
doUpload(){
console.log("Modell:");
console.log(this.bsecFile);
if (this.bsecFile){
// Der folgende Code funktioniert nicht zuverlässig, obwohl er theoretisch funktionieren sollte
// if (this.bsecFile.name.split(".")[1] != "config"){
// console.log("Der Dateiname enthält keine .config - Zurücksetzen.");
// this.bsecFile = null;
// }
// sonst {
console.log("Die Variable scheint in Ordnung zu sein, ich versuche, sie zu veröffentlichen");
var reader = new FileReader();
reader.addEventListener('load', (event) => {
console.log("Datei erfolgreich gelesen! Veröffentlichung (jetzt mit Uint8Array).");
// fn = Dateiname, fs = Dateigröße, fd = (binäre) Dateidaten
const msg = {
fn: this.bsecFile.name,
fs: this.bsecFile.size,
fd: new Uint8Array (event.target.result)
}
console.log("Nachricht ist:");
console.log(msg);
this.$emit("bsec-upload-event", true);
myMqtt.sendWBasePath(sensorSetBsecPath, msg, 1, true);
});
reader.readAsArrayBuffer(this.bsecFile);
}
sonst {
console.log("Die Datei wurde gerade gelöscht, also sollten wir sie entfernen ... $nofile als true senden");
const msg = {
"$nofile": wahr
}
console.log("Nachricht ist:");
console.log(msg);
this.$emit("bsec-upload-event", false);
myMqtt.sendWBasePath(sensorSetBsecPath, msg, 1, true);
}
}
Beachten Sie, dass ich eine Nachricht veröffentliche, die binäre Daten enthält.
Ich verwende einen Konstruktor für ein neues Uint8Array, dem ich event.target.result übergebe - ein ArrayBuffer.
Auf diese Weise werden die Daten genau so weitergegeben, wie sie sind.
Wir verwenden MsgPack, um das JavaScript-Objekt in eine Nachricht zu packen, die binäre Daten unterstützt.
Auf der anderen Seite nimmt der PiCockpit Client die binäre Nachricht entgegen und kann ein (in diesem Fall) Python-Objekt mit den binären Daten wiederherstellen.
Synchronisierung zwischen verschiedenen Browserfenstern möglich machen
Eine zweite Aufgabe besteht darin, den Status über verschiedene Browserfenster und auch Sitzungen des Benutzers hinweg zu synchronisieren.
Wenn der Benutzer zurückkehrt, möchten wir ihm die Datei so präsentieren, als sei sie bereits hochgeladen worden.
Was ich tun möchte, ist im Wesentlichen eine neue File Object-Instanz in JavaScript zu bauen, von meinem Uint8Array, um die Datei aus der MQTT-Nachricht neu zu erstellen!
Wir abonnieren und hören natürlich die MQTT-Nachricht ab. Und dann überwachen wir die Variable auf Änderungen und ändern das Modell bsecFile wie folgt:
globalBsecFileWrapped(val){
// Diese wird NUR dann aktualisiert, wenn wir nicht für das Senden der Nachricht verantwortlich waren.
console.log("watch > globalBsecFileWrapped > contents:");
console.log(this.globalBsecFileWrapped);
console.log("Wir werden dies jetzt als Datei einrichten ...");
if (this.globalBsecFileWrapped.hasOwnProperty("$nofile")){
if (this.globalBsecFileWrapped['$nofile']){
// Aktualisieren Sie es auf null - es wurde von einem anderen Browser zurückgesetzt.
this.bsecFile = null;
}
}
else if (this.globalBsecFileWrapped.hasOwnProperty("fd") && this.globalBsecFileWrapped.hasOwnProperty("fn")) {
// Versuch 2
// this.bsecFile = new File(this.globalBsecFileWrapped.fd, this.globalBsecFileWrapped.fn);
this.bsecFile = new Blob([this.globalBsecFileWrapped.fd], {type: "application/octet-stream"});
this.bsecFile.name = this.globalBsecFileWrapped.fn;
// Fehlersuche
var reader = new FileReader();
reader.addEventListener('load', (event) => {
console.log("Debug >> Datei erfolgreich gelesen! publishing (jetzt mit Uint8Array).");
// fn = Dateiname, fs = Dateigröße, fd = (binäre) Dateidaten
const msg = {
fn: this.bsecFile.name,
fs: this.bsecFile.size,
fd: new Uint8Array (event.target.result)
}
console.log("Nachricht ist:");
console.log(msg);
});
reader.readAsArrayBuffer(this.bsecFile);
}
}
Beachten Sie hier die wichtigen Teile:
this.bsecFile = new Blob([this.globalBsecFileWrapped.fd], {type: "application/octet-stream"});
this.bsecFile.name = this.globalBsecFileWrapped.fn;
Um also die Datei neu zu erstellen (und die vuetify v-file-input auf die richtige Datei zu aktualisieren (mit entsprechender Größe und Dateiname), erstellen wir einen neuen Blob.
Wichtig: der Blob muss den Typ "application/octet-stream" haben - sonst wird er als etwas anderes interpretiert und die Dateigröße stimmt nicht mehr überein!
Und schließlich legen wir den neuen Namen fest - das war's!