Ao gerar um byte[] de uma imagem, a mesma perde qualidade?

Boa tarde, estou salvando um byte[] de uma imagem no postgres, mas ao recuperar esses bytes e converter para um base64 percebi que a qualidade da foto ficou horrível. Tem alguma forma de contornar esse problema?

A única explicação para perder qualidade é que durante a gravação ou durante a leitura você esteja convertendo a imagem para um formato diferente do original.
Posta o código de como você está gravando os bytes desta imagem e o código de como está carregando a imagem do banco.

1 curtida

Ao adicionar um arquivo ele me retorna essa estrutura

Screenshot_4

Então estou pegando a property thumbUrl e aplicando nessa função para converter base64 para byteArray:

export const base64ToArrayBuffer = (base64) => {
    try {
        const bString = window.atob(base64);
        const bLength = bString.length;
        const bytes = new Uint8Array(bLength);
        for (let i = 0; i < bLength; i++) {
            bytes[i] = bString.charCodeAt(i);
        }
        return bytes;
    } catch (e) {
        console.log(e);
    }
};

A chamada do método está desta forma: Obs: sem o replace ele não consegue converter para o byte[].

bytes: [...base64ToArrayBuffer(file.thumbUrl.replace("data:image/png;base64,", ""))]

Ao chegar no backend eu implementei os métodos do MultipartFile para poder pegar algumas informações da imagem ao salvar.

if (params.getFiles() != null && !params.getFiles().isEmpty()) {
    params.getFiles().forEach((entry) -> {
        MultipartFile file;
        try {
            file = new ByteArrayToMultipartFile(entry.getBytes());
            DocumentoFuncionario documentoFuncionario = new DocumentoFuncionario();
            documentoFuncionario.setArquivo(file.getBytes());
            documentoFuncionario.setDataCadastro(new Date());
            documentoFuncionario.setDescricao(entry.getDescricao());
            documentoFuncionario.setFuncionario(entidade);
            documentoFuncionarioRepository.save(documentoFuncionario);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    });
}

Ao retornar para o frontend os bytes da imagem, estou inserindo a parte de texto que removi data:image/png;base64, e chamando este método para criar o file object novamente:

export const base64ToArrayBuffer = (base64) => {
    try {
        const bString = window.atob(base64);
        const bLength = bString.length;
        const bytes = new Uint8Array(bLength);
        for (let i = 0; i < bLength; i++) {
            bytes[i] = bString.charCodeAt(i);
        }
        return bytes;
    } catch (e) {
        console.log(e);
    }
};

Talvez meio gambiarrento, mas não consegui fazer de outra forma.

Fala Breno! Cara, pegar a thumb não é a melhor opção nao, geralmente a gente pega o objeto File, envia pro back e ele se vira, da pra vc gerar um base64 do File também

1 curtida

E aí meu grande amigo @rodriguesabner, pois é mano, eu tava tentando enviar o file inteiro, mas me buguei na criação da API para ser JSON/mult-partdata aí fiz dessa forma para mandar JSON. Não vai ter pra onde correr vou ter que criar duas chamadas de APIs pra cada caso quando for JSON quando for form data. Acredito que assim resolva o problema.

1 curtida

exemplo do formData no front:

  function handleImageChange(e) {
       const {files} = e.target;

       if (files && files[0]) {
           let reader = new FileReader();
           setImage(files[0]);
  
           // caso queira mostrar a imagem que foi upada 
           // em algum canto
           reader.onload = async function (e) {
               setAvatar(e.target.result);
           };

           reader.readAsDataURL(files[0]);
       }
   }

   async function sendImageToServer() {
       const formData = new FormData();
       formData.append("propriedade-q-vc-espera-no-back", image); //variavel q a imagem foi armazenada;

       await api.post(`/endpoint`, formData);
   }
<input onChange={(e) => handleImageChange(e)} ... />
1 curtida

Tinha visto uns exemplos, eu não tava querendo usar o form data porque eu vou ter que formatar n variáveis entre listas, objetos, listas de long, listas de objetos. A entidade é enorme, por isso estava querendo não usar o form data já que estava tudo estruturado em JSON, mas não vai ter jeito vou ter que usar ele para poder pegar o multpartfile no backend aí não tem erro, da tudo certinho.

Boa tarde pessoal, estou reabrindo esse topico porque mesmo salvando um MultpartFile como foi sujerido e pasando os formatos corretos, ao retornar o base64 a qualidade fica muito prejudicada. Dando uma inspecionada na imagem percebi que ela foi muito reduziu de tamanho. Acredito que essa seja a raiz do problema.

image

Meu método para salvar é este:

documento.setArquivo(params.getFiles().get(i).getBytes());
documento.setDataCadastro(new Date());
documento.setDescricao(params.getDescricoes().get(i));
documento.setTipoArquivo(params.getFiles().get(i).getContentType());

Meu método para retornar o base64 é este:

Map<String, String> model = new HashMap<>();
String encoder = Base64.getEncoder().encodeToString(entry.getArquivo());
model.put("base64", encoder);
model.put("descricao", entry.getDescricao());

Alguém saberia me informar por que a imagem está sendo salva 200x200?

Provavelmente porque você está enviando ela pequena para seu backend.

1 curtida

Boa noite, era isto mesmo, não sei porque, mas o JavaScript está reduzindo. Obrigado.