Sincronizar carregamentos de arquivos entre as janelas do navegador
Para PiCockpitEstou actualmente a desenvolver a aplicação Nariz Digital.
Este aplicativo requer um upload para o arquivo de configuração BSEC - que são dados binários.
Uma vez que o usuário carrega o arquivo, ele é publicado via MQTT, e o picockpit-cliente pode usá-lo para configurar o algoritmo BSEC AI para fazer a detecção de gás.
Aqui está uma pequena amostra de parte da webinterface:
Vuetify navios com um elemento GUI embutido, v-file-input:
<v-file-input
v-model="bsecFile"
label="Arquivo de Configuração BSEC do AI Studio (.config)".
placeholder="Carregue aqui o seu ficheiro de configuração BSEC".
prepend-icon="mdi-paperclip"
delineado
denso
tamanho do show-size
accept=".config"
@change="doUpload
>
</v-file-input>
Neste caso, bsecFile é o meu modelo.
Nota: Como é um modelo, também pode ser escrito para!
Publicação no MQTT
Esta é a aparência do código doUpload (ainda incluindo informações de debug):
doUpload(){
console.log("modelo:");
console.log(este.bsecFile);
if (this.bsecFile){
// o código abaixo não funciona de forma fiável, embora em teoria deva
// if (this.bsecFile.name.split(".")[1] != "config"){
// console.log("O nome do arquivo não inclui .config - resetting");
// this.bsecFile = nulo;
// }
// senão {
console.log("a variável parece estar OK, tentando publicar");
var reader = novo FileReader();
reader.addEventListener('load', (event) => {
console.log("File read successfully! publishing (agora com Uint8Array)");
// fn = nome do arquivo, fs = tamanho do arquivo, fd = dados do arquivo (binário)
const msg = {
fn: este.bsecFile.name,
fs: este.bsecFile.tamanho,
fd: novo Uint8Array (event.target.result)
}
console.log("mensagem é:");
console.log(msg);
this.$emit("bsec-upload-event", true);
myMqtt.sendWBasePath(sensorSetBsecPath, msg, 1, true);
});
reader.readAsArrayBuffer(this.bsecFile);
}
senão (
console.log("o arquivo foi limpo, então devemos removê-lo ... enviando $nofile como verdadeiro");
const msg = {
"$nofile": verdadeiro
}
console.log("mensagem é:");
console.log(msg);
this.$emit("bsec-upload-event", falso);
myMqtt.sendWBasePath(sensorSetBsecPath, msg, 1, true);
}
}
Note que eu publico uma mensagem que inclui dados binários.
Eu uso um construtor para um novo Uint8Array ao qual passo event.target.result - um ArrayBuffer.
Desta forma, os dados são transmitidos precisamente como estão.
Nós usamos o MsgPack para empacotar o objeto JavaScript em uma mensagem - que suporta dados binários.
Por outro lado, PiCockpit Client pega a mensagem binária e pode reintegrar um objeto Python (nesse caso), incluindo os dados binários.
Fazendo-o funcionar para sincronizar através das janelas do navegador
Uma segunda tarefa é sincronizar o estado em diferentes janelas do navegador e também as sessões do usuário.
Quando o usuário retornar, gostaríamos de apresentar o arquivo como já tendo sido carregado para eles.
O que eu quero fazer é essencialmente construir uma nova instância File Object em JavaScript, a partir do meu Uint8Array para recriar o ficheiro a partir da mensagem MQTT!
Nós subscrevemos e ouvimos a mensagem do MQTT, é claro. E depois monitoramos a variável para alterações, e modificamos o modelo bsecFile da seguinte forma:
globalBsecFileWrapped(val){
// isto só será atualizado se não formos responsáveis por enviar a mensagem em primeiro lugar.
console.log("watch > globalBsecFileWrapped > contents:");
console.log(this.globalBsecFileWrapped);
console.log("vamos definir isto como um arquivo agora ... ");
if (this.globalBsecFileWrapped.hasOwnProperty("$nofile")){
if (this.globalBsecFileWrapped['$nofile']){
// atualizá-lo para ser nulo - foi reinicializado a partir de um navegador diferente.
este.bsecFile = nulo;
}
}
senão se (this.globalBsecFileWrapped.hasOwnProperty("fd") && this.globalBsecFileWrapped.hasOwnProperty("fn")) {
// tentativa 2
// this.bsecFile = novo arquivo(this.globalBsecFileWrapped.fd, this.globalBsecFileWrapped.fn);
this.bsecFile = novo Blob([this.globalBsecFileWrapped.fd], {type: "application/octet-stream"});
this.bsecFile.name = this.globalBsecFileWrapped.fn;
// debug
var reader = novo FileReader();
reader.addEventListener('load', (event) => {
console.log("Debug >> Publicação de arquivo lido com sucesso! (agora com Uint8Array)");
// fn = nome do arquivo, fs = tamanho do arquivo, fd = dados do arquivo (binário)
const msg = {
fn: este.bsecFile.name,
fs: este.bsecFile.tamanho,
fd: novo Uint8Array (event.target.result)
}
console.log("mensagem é:");
console.log(msg);
});
reader.readAsArrayBuffer(this.bsecFile);
}
}
anote as partes importantes aqui:
this.bsecFile = novo Blob([this.globalBsecFileWrapped.fd], {type: "application/octet-stream"});
this.bsecFile.name = this.globalBsecFileWrapped.fn;
Então, a fim de recriar o arquivo (e ter o vuetify v-file-input atualizado para o arquivo correto (mostrando o tamanho adequadamente, e nome do arquivo) estamos construindo um novo Blob.
Importante: o Blob precisa ter o tipo de "application/octet-stream" - caso contrário ele será interpretado como algo diferente e o tamanho dos arquivos não será mais compatível!
E finalmente, definimos o novo nome - é isso!