Blob – Manipulando dados binários em javascript

Essa semana, me deparei com uma situação nova enquanto desenvolvia uma extensão para google chrome com o Quasar Framework. Precisava guardar imagens e hyperlinks na área de transferência (clipboard) para serem colados posteriormente num wealthy textual content editor. Mais especificamente, fotos do Instagram e um hyperlink para o perfil do autor.

Após alguma pesquisa, vi que a solução passava pela utilização da classe Blob, da File Api.



TLDR;

A solução que utilizei foi enviar pra área de transferência um Blob contendo o html formatado com as imagens e hyperlinks. Para isso, o atributo src das imagens precisa conter information urls em vez de urls comuns.

Estes foram os passos utilizados:

  1. Obter a imagem e convertê-la em information url:
const img = doc.createElement('img');
const response = await fetch('https://through.placeholder.com/350x150');

// a response do fetch api já te devolve um blob
const blob = await response.blob(); 

// FileReader é responsável por gerar an information url em base64
const reader = new FileReader();
reader.readAsDataURL(blob);
reader.onload = () => {
    img.src = reader.end result;
}
// insere a imagem no physique, apenas para exemplificar.
doc.physique.appendChild(img);

Enter fullscreen mode

Exit fullscreen mode

  1. Transformar o html com a imagem em blob:
const htmlBlob = new Blob([document.body.innerHTML], {sort: 'textual content/html'});
Enter fullscreen mode

Exit fullscreen mode

  1. Escrever o html na área de transferência through navigator api:
// observe que pode ser necessário solicitar permissão pra escrever na clipboard
const { state } = await navigator.permissions.question({
                    identify: 'clipboard-write',
       });
if(state === 'granted'){
    await navigator.clipboard.write([
            new ClipboardItem({ 'text/html': htmlBlob })
    ]);
}
Enter fullscreen mode

Exit fullscreen mode



O que é um Blob?

Do inglês Binary Massive Object, um blob representa um conjunto de dados binários, normalmente associados a um MIME sort (textual content/html; picture/png; software/pdf and so on.).

Podemos entender “dado binário”, como o oposto de dado puramente textual (textual content/plain). No fim das contas, ambos são bits armazenados em memória. Porém, dados textuais precisam de um encoding para “fazer sentido”, a exemplo do UTF-8 ou do ANSI.

Os blobs, por si só, não permitem muita interação com seu conjunto de dados. Usualmente são utilizados junto com demais courses, como a URL, que permite a criação de urls apontando para um blob, útil para usar em elementos como , que aceita uma url como seu supply.

Anatomia do Blob:

Tamanho whole, em bytes. Observe que, isso está mais para o size de um array do quê para o size de uma string. Isso porque existem caracteres que ocupam mais de um byte.

new Blob(["à"], {sort: "textual content/plain"})
// Blob {measurement: 2, sort: 'textual content/plain'}

new Blob(["a"], {sort: "textual content/plain"})
// Blob {measurement: 1, sort: 'textual content/plain'}

Enter fullscreen mode

Exit fullscreen mode

MIME sort associado ao blob.



Blob URLs

Como dito acima, é possível gerar uma url referenciando o conteúdo de um blob. Isso pode ser feito de duas maneiras:



createObjectURL

A primeira é através da classe URL, chamando o método createObjectURL( blob ). Esse método retorna uma url no formato blob:https://site-de-origem-do-blob/identificador-do-blob. Esse recurso fica armazenado numa área especial do browser, enquanto o objeto que criou o blob existir. Por isso, não é possível acessar o conteúdo do blob se a aba onde ele foi gerado for fechada, por exemplo. Além disso, é necessário chamar o revokeObjectURL( blobUrl ) tão emblem o recurso não seja mais necessário. Caso contrário, o blob não será removido da memória pelo rubbish collector enquanto o recurso que o criou existir. Isso é um problema, a depender do tamanho/quantidade de blobs em memória.

// blob de string
const blob = new Blob(["uma string qualquer"], {sort: "textual content/plain"});
const url = URL.createObjectURL(blob);

console.log(blob);
// Blob {measurement: 19, sort: 'textual content/plain'}

window.open(url, '_blank');

// libera os bytes do blob reservados na memória pelo browser
URL.revokeObjectURL(url);

Enter fullscreen mode

Exit fullscreen mode

// blob de um png
fetch('https://www.google.com.br/photographs/branding/googlelogo/1x/googlelogo_color_272x92dp.png')
.then(response => response.blob())
.then(blob => {
        console.log(blob);
        // Blob {measurement: 5969, sort: 'picture/png'}
    const url = URL.createObjectURL(blob);
    window.open(url, '_blank');
})
Enter fullscreen mode

Exit fullscreen mode

Print do blob de png



readAsDataURL

A segunda forma de obter o blob como url é gerar uma Information URL com seu conteúdo. Information URLs são como arquivos embutidos, representados por uma URL. São formadas por 4 partes: information:[<mediatype>][;base64],<information>. information: é o prefixo, assim como blob:; mediatype é o MIME sort do dado; base64 indica se os dados foram codificados em base64, que é uma representação textual de dados binários. É possível não usar base64, mas os dados tem que ser URL Encoded, no entanto, isso foge do nosso escopo no momento.

Para transformar um blob em Information URL, utilizamos a classe FileReader, e seu método readAsDataURL.

// blob de um png
fetch('https://www.google.com.br/photographs/branding/googlelogo/1x/googlelogo_color_272x92dp.png')
.then(response => response.blob())
.then(blob => {
        console.log(blob);
        // Blob {measurement: 5969, sort: 'picture/png'}

        const fileReader = new FileReader();
        fileReader.onload = () => {
            console.log(fileReader.end result)
            // information:picture/png;base64,iVBORw0KGgoAAAANSUhEUgAAARAAAABcCAYAAACm5+q2AAAXGElEQVR4Ae1dC5QcVZm+OtOBwC6CwiqCCBIQkAWSqpqEk[...]

        }
        fileReader.readAsDataURL(blob);
})
Enter fullscreen mode

Exit fullscreen mode

A url gerada pode ser atribuída ao src de qualquer elemento.



Obtain de Arquivos

Algumas restrições de segurança se aplicam às information urls. Como exemplo, não é possível forçar o obtain de arquivos nesse formato, nem abrir uma information url em uma nova aba. Os browsers tratam elas, de fato, como uma “origin” diferente. Tudo isso está relacionado ao bloqueio do chamado prime stage navigation. Dessa forma, a maneira mais conveniente de efetuar obtain programático é utilizar uma blob url.

fetch('https://through.placeholder.com/350x150')
.then(response => response.blob())
.then(blob => {
    const hyperlink = doc.createElement('a');
        hyperlink.obtain = 'blob.png';
        hyperlink.href = URL.createObjectURL(blob);
        hyperlink.click on();
        URL.revokeObjectURL(hyperlink.href);
})
Enter fullscreen mode

Exit fullscreen mode



Considerações Finais

Usar um ou outro método de obtenção de URL fica a critério de cada aplicação. No meu caso, as imagens precisavam ser embutidas em outros documentos, em um editor fora do navegador. Dessa forma, não seria possível usar uma blob url. Observe, entretanto, que o processo de gerar a Information Url pode ser mais lento que apenas referenciar uma área de memória, como faz o browser com a blob url. Além disso, as especificações de tamanho máximo do conteúdo contido numa information url variam de acordo ao navegador, partindo de cerca de 65535 caracteres até 2GB.

Para saber mais a respeito, recomendo a leitura de: https://javascript.info/blob
https://style-tricks.com/kennethlum/understanding-what-a-blob-is-35ga

Add a Comment

Your email address will not be published. Required fields are marked *