Synchroniseren van bestandsuploads tussen browservensters
Voor PiCockpitIk ben momenteel de Digitale Neus app aan het ontwikkelen.
Deze app vereist een upload voor het BSEC-configuratiebestand - dat binaire gegevens zijn.
Zodra de gebruiker het bestand uploadt, wordt het gepubliceerd via MQTT, en picockpit-client kan het gebruiken om het BSEC AI-algoritme te configureren om gasdetectie te doen.
Hier is een klein voorproefje van een deel van de webinterface:
Vuetify wordt geleverd met een ingebouwd GUI element, v-file-input:
<v-bestand-input
v-model="bsecFile"
label="BSEC Configuratiebestand van AI Studio (.config)"
placeholder="Upload hier uw BSEC-configuratiebestand"
prepend-icon="mdi-paperclip"
geschetst
dicht
show-size
accepteren=".config"
@change="doUpload"
>
</v-file-input>
In dit geval, bsecFile is mijn model.
Opmerking: Aangezien het een model is, kan er ook naar worden geschreven!
Publiceren op MQTT
Dit is hoe de handler code doUpload eruit ziet (nog steeds inclusief debug info):
doUpload(){
console.log("model:");
console.log(this.bsecFile);
als (dit.bsecFile){
// de onderstaande code werkt niet betrouwbaar, hoewel het in theorie zou moeten
// if (this.bsecFile.name.split(".")[1] != "config"){
// console.log("De bestandsnaam bevat geen .config - opnieuw instellen.");
// this.bsecFile = null;
// }
// anders {
console.log("de variabele lijkt in orde te zijn, ik probeer te publiceren");
var lezer = nieuwe FileReader();
reader.addEventListener('load', (event) => {
console.log("Bestand succesvol gelezen! publiceren (nu met Uint8Array).");
// fn = bestandsnaam, fs = bestandsgrootte, fd = (binaire) bestandsgegevens
const msg = {
fn: dit.bsecFile.name,
fs: dit.bsecFile.size,
fd: nieuwe Uint8Array (event.target.result)
}
console.log("bericht is:");
console.log(msg);
this.$emit("bsec-upload-event", true);
myMqtt.sendWBasePath(sensorSetBsecPath, msg, 1, true);
});
reader.readAsArrayBuffer(this.bsecFile);
}
anders {
console.log("het bestand is zojuist gewist, dus we moeten het verwijderen ... $nofile als waar verzenden");
const msg = {
"$nofile": true
}
console.log("bericht is:");
console.log(msg);
this.$emit("bsec-upload-event", false);
myMqtt.sendWBasePath(sensorSetBsecPath, msg, 1, true);
}
}
Merk op dat ik een bericht publiceer dat binaire gegevens bevat.
Ik gebruik een constructor voor een nieuwe Uint8Array waaraan ik event.target.result doorgeef - een ArrayBuffer.
Op deze manier worden de gegevens doorgegeven precies zoals ze zijn.
We gebruiken MsgPack om het JavaScript Object in te pakken in een bericht - dat binaire gegevens ondersteunt.
Aan de andere kant neemt PiCockpit Client de binaire boodschap aan, en kan een (in dat geval) Python object opnieuw aanmaken, inclusief de binaire gegevens.
Het laten werken voor synchronisatie tussen browser vensters
Een tweede taak is het synchroniseren van de toestand over verschillende browservensters en ook sessies van de gebruiker.
Wanneer de gebruiker terugkeert, willen wij het bestand presenteren alsof het reeds naar hem is geüpload.
Wat ik wil doen is in essentie een nieuwe File Object instantie bouwen in JavaScript, van mijn Uint8Array om het bestand opnieuw te maken van het MQTT-bericht!
We abonneren ons en luisteren naar het MQTT bericht, natuurlijk. En dan controleren we de variabele op veranderingen, en wijzigen het model bsecFile als volgt:
globalBsecFileWrapped(val){
// dit zal ALLEEN worden bijgewerkt als wij niet verantwoordelijk waren voor het verzenden van het bericht in de eerste plaats.
console.log("watch > globalBsecFileWrapped > contents:");
console.log(this.globalBsecFileWrapped);
console.log("we gaan dit nu als een bestand instellen ... ");
als (this.globalBsecFileWrapped.hasOwnProperty("$nofile")){
als (this.globalBsecFileWrapped['$nofile']){
// update het zodat het null is - het werd gereset vanuit een andere browser.
dit.bsecFile = null;
}
}
anders als (this.globalBsecFileWrapped.hasOwnProperty("fd") && this.globalBsecFileWrapped.hasOwnProperty("fn")) {
// poging 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 lezer = nieuwe FileReader();
reader.addEventListener('load', (event) => {
console.log("Debug >> Bestand succesvol gelezen! publiceren (nu met Uint8Array).");
// fn = bestandsnaam, fs = bestandsgrootte, fd = (binaire) bestandsgegevens
const msg = {
fn: dit.bsecFile.name,
fs: dit.bsecFile.size,
fd: nieuwe Uint8Array (event.target.result)
}
console.log("bericht is:");
console.log(msg);
});
reader.readAsArrayBuffer(this.bsecFile);
}
}
let op de belangrijke delen hier:
this.bsecFile = new Blob([this.globalBsecFileWrapped.fd], {type: "application/octet-stream"});
this.bsecFile.name = this.globalBsecFileWrapped.fn;
Dus, om het bestand opnieuw te maken (en de vuetify v-file-input te laten updaten naar het juiste bestand (met de juiste grootte, en bestandsnaam) maken we een nieuwe Blob.
Belangrijk: de Blob moet het type "application/octet-stream" hebben - anders wordt het geïnterpreteerd als iets anders en zal de bestandsgrootte niet meer overeenkomen!
En tenslotte, stellen we de nieuwe naam in - dat is het!