Sincronizzazione del caricamento dei file tra le finestre del browser
Per PiCockpitAttualmente sto sviluppando l'applicazione Digital Nose.
Questa applicazione richiede un upload per il file di configurazione BSEC - che è un dato binario.
Una volta che l'utente carica il file, questo viene pubblicato via MQTT, e picockpit-client può usarlo per configurare l'algoritmo BSEC AI per fare il rilevamento dei gas.
Ecco una piccola anteprima di una parte dell'interfaccia web:
Vuetify viene fornito con un elemento GUI incorporato, v-file-input:
<v-file-input
v-model="bsecFile"
label="File di configurazione BSEC da AI Studio (.config)"
placeholder="Carica il tuo file di configurazione BSEC qui"
prepend-icon="mdi-paperclip"
delineato
denso
show-size
accept=".config"
@change="doUpload"
>
</v-file-input>
In questo caso, bsecFile è il mio modello.
Nota: poiché è un modello, può anche essere scritto!
Pubblicazione su MQTT
Ecco come appare il codice del gestore doUpload (che include ancora le informazioni di debug):
doUpload(){
console.log("modello:");
console.log(this.bsecFile);
se (this.bsecFile){
// il codice qui sotto non funziona in modo affidabile, anche se in teoria dovrebbe
// if (this.bsecFile.name.split(".")[1] != "config"){
// console.log("Il nome del file non include .config - reset.");
// this.bsecFile = null;
// }
// else {
console.log("la variabile sembra essere OK, provando a pubblicare");
var reader = nuovo FileReader();
reader.addEventListener('load', (event) => {
console.log("File letto con successo! pubblicazione (ora con Uint8Array)");
// fn = nome del file, fs = dimensione del file, fd = dati (binari) del file
const msg = {
fn: this.bsecFile.name,
fs: this.bsecFile.size,
fd: nuovo Uint8Array (event.target.result)
}
console.log("il messaggio è:");
console.log(msg);
this.$emit("bsec-upload-event", true);
myMqtt.sendWBasePath(sensorSetBsecPath, msg, 1, true);
});
reader.readAsArrayBuffer(this.bsecFile);
}
else {
console.log("il file è stato appena cancellato, quindi dovremmo rimuoverlo... inviando $nofile come true");
const msg = {
"$nofile": vero
}
console.log("il messaggio è:");
console.log(msg);
this.$emit("bsec-upload-event", false);
myMqtt.sendWBasePath(sensorSetBsecPath, msg, 1, true);
}
}
Notate che pubblico un messaggio che include dati binari.
Uso un costruttore per un nuovo Uint8Array a cui passo event.target.result - un ArrayBuffer.
In questo modo, i dati vengono trasmessi esattamente come sono.
Usiamo MsgPack per impacchettare l'oggetto JavaScript in un messaggio - che supporta i dati binari.
Dall'altro lato, PiCockpit Client prende il messaggio binario, e può reintegrare un oggetto Python (in questo caso), compresi i dati binari.
Farlo funzionare per la sincronizzazione attraverso le finestre del browser
Un secondo compito è quello di sincronizzare lo stato attraverso diverse finestre del browser e anche sessioni dell'utente.
Quando l'utente ritorna, vorremmo presentare il file come se fosse già stato caricato.
Quello che voglio fare è essenzialmente costruire una nuova istanza dell'oggetto File in JavaScript, dal mio Uint8Array per ricreare il file dal messaggio MQTT!
Sottoscriviamo e ascoltiamo il messaggio MQTT, ovviamente. E poi monitoriamo la variabile per i cambiamenti, e modifichiamo il modello bsecFile come segue:
globalBsecFileWrapped(val){
// questo sarà aggiornato solo se non siamo stati responsabili dell'invio del messaggio in primo luogo.
console.log("watch > globalBsecFileWrapped > contents:");
console.log(this.globalBsecFileWrapped);
console.log("stiamo per impostare questo come un file ora ... ");
se (this.globalBsecFileWrapped.hasOwnProperty("$nofile")){
se (this.globalBsecFileWrapped['$nofile']){
// aggiornalo per essere nullo - è stato resettato da un browser diverso.
this.bsecFile = null;
}
}
else if (this.globalBsecFileWrapped.hasOwnProperty("fd") && this.globalBsecFileWrapped.hasOwnProperty("fn")) {
// tentativo 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;
// debug
var reader = nuovo FileReader();
reader.addEventListener('load', (event) => {
console.log("Debug >> File letto con successo! pubblicazione (ora con Uint8Array)");
// fn = nome del file, fs = dimensione del file, fd = dati (binari) del file
const msg = {
fn: this.bsecFile.name,
fs: this.bsecFile.size,
fd: nuovo Uint8Array (event.target.result)
}
console.log("il messaggio è:");
console.log(msg);
});
reader.readAsArrayBuffer(this.bsecFile);
}
}
notare le parti importanti qui:
this.bsecFile = new Blob([this.globalBsecFileWrapped.fd], {type: "application/octet-stream"});
this.bsecFile.name = this.globalBsecFileWrapped.fn;
Quindi, per ricreare il file (e far sì che il vuetify v-file-input si aggiorni al file corretto (mostrando adeguatamente la dimensione e il nome del file) stiamo costruendo un nuovo Blob.
Importante: il Blob deve avere il tipo "application/octet-stream" - altrimenti sarà interpretato come qualcosa di diverso e il filesize non corrisponderà più!
E infine, impostiamo il nuovo nome - questo è tutto!