DEV Community

Cover image for Blob - Manipulando dados binários em javascript
Flávio Lopes
Flávio Lopes

Posted on

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 links na área de transferência (clipboard) para serem colados posteriormente num rich text editor. Mais especificamente, fotos do Instagram e um link 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 links. Para isso, o atributo src das imagens precisa conter data urls em vez de urls comuns.

Estes foram os passos utilizados:

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

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

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

Enter fullscreen mode Exit fullscreen mode
  1. Transformar o html com a imagem em blob:
const htmlBlob = new Blob([document.body.innerHTML], {type: 'text/html'});
Enter fullscreen mode Exit fullscreen mode
  1. Escrever o html na área de transferência via navigator api:
// note que pode ser necessário solicitar permissão pra escrever na clipboard
const { state } = await navigator.permissions.query({
                    name: '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 Large Object, um blob representa um conjunto de dados binários, normalmente associados a um MIME type (text/html; image/png; application/pdf etc.).

Podemos entender “dado binário”, como o oposto de dado puramente textual (text/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 classes, 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 source.

Anatomia do Blob:

  • size

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

new Blob(["à"], {type: "text/plain"})
// Blob {size: 2, type: 'text/plain'}

new Blob(["a"], {type: "text/plain"})
// Blob {size: 1, type: 'text/plain'}

Enter fullscreen mode Exit fullscreen mode
  • type

MIME type 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 logo o recurso não seja mais necessário. Caso contrário, o blob não será removido da memória pelo garbage 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"], {type: "text/plain"});
const url = URL.createObjectURL(blob);

console.log(blob);
// Blob {size: 19, type: 'text/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

Print do blob de texto

// blob de um png
fetch('https://www.google.com.br/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png')
.then(response => response.blob())
.then(blob => {
        console.log(blob);
        // Blob {size: 5969, type: 'image/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 Data URL com seu conteúdo. Data URLs são como arquivos embutidos, representados por uma URL. São formadas por 4 partes: data:[<mediatype>][;base64],<data>. data: é o prefixo, assim como blob:; mediatype é o MIME type 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 Data URL, utilizamos a classe FileReader, e seu método readAsDataURL.

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

        const fileReader = new FileReader();
        fileReader.onload = () => {
            console.log(fileReader.result)
            // data:image/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.

Download de Arquivos

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

fetch('https://via.placeholder.com/350x150')
.then(response => response.blob())
.then(blob => {
    const link = document.createElement('a');
        link.download = 'blob.png';
        link.href = URL.createObjectURL(blob);
        link.click();
        URL.revokeObjectURL(link.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. Note, entretanto, que o processo de gerar a Data 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 data 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://dev.to/kennethlum/understanding-what-a-blob-is-35ga

Top comments (0)