9
respostas

Como recebo uma imagem do Banco de Dados.

Bom dia!!! Pessoal, gostaria de uma ajuda. Bem, fiz uma api com Node.js usando fastify para salvar uns dados em um banco de dados Postgress (Neon) que também contém imagem. Parece que o Postgres não suporta BLOB e sim BYTEA. Consegui salvar a imagem com BYTEA, aparece o nome a extensão, tudo certinho. O problema e que quando busco esses dados com um app em Flutter essa imagem vem como um objeto do tipo buffer e um array, acredito que seja de bytes. Gostaria de saber como faço para converter isso e receber a devida imagem salva no database?

Obrigado!

9 respostas

Oi Gustavo,

Entendo sua dificuldade em lidar com o formato BYTEA do PostgreSQL ao receber dados no Flutter. 🙄

O que acontece é que o banco de dados envia os dados da imagem como um array de bytes, e você precisa convertê-los para um formato que o Flutter possa exibir como imagem.

No Flutter, você pode usar o Uint8List para representar esses bytes e o Image.memory para construir a imagem a partir dele.

Veja um exemplo prático:

import 'dart:typed_data';
import 'package:flutter/material.dart';

// Supondo que 'bytesDaImagem' seja o array de bytes recebido do banco de dados
Uint8List bytesDaImagem = ...; // Substitua pelos seus bytes

Image imagem = Image.memory(bytesDaImagem);

// Para exibir a imagem em um widget:
Container(
  child: imagem,
);

Basicamente, você precisa receber o array de bytes, convertê-lo para Uint8List e usar o Image.memory para criar o widget de imagem. 💪

Para saber mais: Documentação oficial do Flutter sobre Image.memory. Aqui você encontra detalhes sobre como usar o Image.memory para exibir imagens a partir de bytes.

Continue explorando e aprofundando seus conhecimentos! 🤔

Fala meu querido, obrigado pela resposta super fast, visualizei assim que vc respondeu! Fiz o que vc mencionu, so que o Uint8List não aceitou uma Lista dinamica, ai tive que usar o Uint8List.fronList(arrayDeBytes.cast()) para converter, pois esse método só aceita uma List do tipo inteiro. Mas, ao tentar renderizar, ele da um erro:

android.graphics.ImageDecoder$DecodeException: Failed to create image decoder with message 'unimplemented'Input contained an error.

Desconfiei da api, fiz uma api com express.js, chamando-a, mas esta dando o mesmo erro. Acredito que não é a api. Parece que tem uma outra camada de decodifição que deve ser implementada depois, não sei...

... método que chama a api e retorna abaixo:
return List<ModelQuestions>.from(
  list.map(
    (element) {
      Uint8List bytesImage =
          Uint8List.fromList(element['image']['data'].cast<int>());
      Image image = Image.memory(bytesImage);
      element['image'] = image;
      return ModelQuestions.toMap(element);
    },
  ),
);

Cara, ficarei muito grato caso consiga me ajudar, to quebrando a cabeça aqui faz dias.kkkk coisa de loko! Obrigado!

Oi Gustavo,

O erro que você está encontrando sugere que os bytes não estão em um formato de imagem válido.

Vamos ajustar o código para decodificar corretamente:

return List<ModelQuestions>.from(
  list.map(
    (element) {
      // Primeiro converte para Base64
      String base64String = base64Encode(element['image']['data']);
      // Depois decodifica para bytes
      Uint8List bytesImage = base64Decode(base64String);
      
      element['image'] = Image.memory(
        bytesImage,
        errorBuilder: (context, error, stackTrace) {
          print('Erro ao carregar imagem: $error');
          return Icon(Icons.error);
        },
      );
      
      return ModelQuestions.toMap(element);
    },
  ),
);

Adicione os imports necessários:

import 'dart:convert';
import 'dart:typed_data';

Se ainda persistir, você pode tentar debugar imprimindo os primeiros bytes da imagem para verificar se estão chegando corretamente da API:

print('Primeiros bytes: ${bytesImage.take(10).toList()}');

Torcendo que funcione 👍

Bom dia Luis,

Cara, o erro continua,

E/FlutterJNI(13409): Failed to decode image E/FlutterJNI(13409): android.graphics.ImageDecoder$DecodeException: Failed to create image decoder with message 'unimplemented'Input contained an error.

E/FlutterJNI(13409): at android.graphics.ImageDecoder.nCreate(Native Method) E/FlutterJNI(13409): at android.graphics.ImageDecoder.-$$Nest$smnCreate(Unknown Source:0) E/FlutterJNI(13409): at android.graphics.ImageDecoder$ByteBufferSource.createImageDecoder(ImageDecoder.java:254) E/FlutterJNI(13409): at android.graphics.ImageDecoder.decodeBitmapImpl(ImageDecoder.java:1981) E/FlutterJNI(13409): at android.graphics.ImageDecoder.decodeBitmap(ImageDecoder.java:1973) E/FlutterJNI(13409): at io.flutter.embedding.engine.FlutterJNI.decodeImage(FlutterJNI.java:561) D/EGL_emulation(13409): app_time_stats: avg=75.81ms min=4.83ms max=271.84ms count=16 I/flutter (13409): Erro ao carreagr imagem: Exception: Invalid image data

Unica coisa que fiz foi fazer um cast no array de bytes, pois o método base64String reclamou que deve receber uma List de inteiros. Tentei pelo express.js também, mas deu a mesma coisa. Exclui e cadastrei outra imagem de outro formato, mas nada.

return List.from( list.map( (element) { String base64String = base64Encode(element['image']['data'].cast()); Uint8List bytesImage = base64Decode(base64String); element['image'] = Image.memory( bytesImage, errorBuilder: (context, error, stackTrace) { print('Erro ao carreagr imagem: $error'); return Icon(Icons.error); }, ); print('Primeiros bytes: ${bytesImage.take(10).toList()}'); return ModelQuestions.toMap(element); }, ), );

E no print veio isso:
// Primeiros bytes: [49, 55, 51, 53, 54, 52, 55, 55, 55, 48]

Gustavo,

Pedi ajuda aos universitários 😜

Espero que meus amigos te ajudem e que 2025 seja um ano com menos Bugs e mais felicidade 🎆

Deepseek

O problema parece estar relacionado à forma como os bytes da imagem estão sendo manipulados ou ao formato dos dados que estão sendo enviados do backend. Vamos tentar resolver isso passo a passo.

1. Verifique os Bytes da Imagem

Primeiro, vamos garantir que os bytes da imagem estão corretos e não estão sendo corrompidos durante o processo.

No Backend (Node.js):

const fs = require('fs');
const imageBytes = fs.readFileSync('caminho/para/sua/imagem.jpg');
console.log('Primeiros bytes da imagem:', imageBytes.slice(0, 10));

No Flutter:

print('Primeiros bytes recebidos: ${bytesImage.take(10).toList()}');

Compare os primeiros bytes logados no backend com os primeiros bytes recebidos no Flutter. Eles devem ser idênticos. Se forem diferentes, há um problema na transmissão dos dados.

2. Use Base64 para Transmissão

Como você já tentou usar Base64, vamos garantir que isso seja feito corretamente.

No Backend (Node.js):

const base64Image = imageBytes.toString('base64');
console.log('Base64 da imagem:', base64Image);

No Flutter:

String base64String = ...; // Receba a string Base64
Uint8List bytesImage = base64Decode(base64String);
print('Primeiros bytes decodificados: ${bytesImage.take(10).toList()}');

3. Verifique o Formato da Imagem

Certifique-se de que o formato da imagem (JPEG, PNG, etc.) é suportado pelo Image.memory. Você pode tentar usar uma imagem simples em formato PNG para teste.

4. Exiba a Imagem no Flutter

Agora, vamos tentar exibir a imagem no Flutter.

Image imagem = Image.memory(
  bytesImage,
  errorBuilder: (context, error, stackTrace) {
    print('Erro ao carregar imagem: $error');
    return Icon(Icons.error);
  },
);

// Para exibir a imagem em um widget:
Container(
  child: imagem,
);

5. Teste com uma Imagem Simples

Para descartar problemas com a imagem específica que você está usando, tente com uma imagem simples e pequena.

No Backend (Node.js):

const fs = require('fs');
const imageBytes = fs.readFileSync('caminho/para/sua/imagem_simples.png');
const base64Image = imageBytes.toString('base64');
console.log('Base64 da imagem simples:', base64Image);

No Flutter:

String base64String = ...; // Receba a string Base64 da imagem simples
Uint8List bytesImage = base64Decode(base64String);
Image imagem = Image.memory(bytesImage);

// Para exibir a imagem em um widget:
Container(
  child: imagem,
);

6. Verifique o Código Completo

Aqui está um exemplo completo de como você pode fazer isso:

No Backend (Node.js):

const fs = require('fs');
const express = require('express');
const app = express();

app.get('/image', (req, res) => {
  const imageBytes = fs.readFileSync('caminho/para/sua/imagem.jpg');
  const base64Image = imageBytes.toString('base64');
  res.json({ image: base64Image });
});

app.listen(3000, () => {
  console.log('Servidor rodando na porta 3000');
});

No Flutter:

import 'dart:convert';
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;

class ImageTest extends StatefulWidget {
  @override
  _ImageTestState createState() => _ImageTestState();
}

class _ImageTestState extends State<ImageTest> {
  Uint8List? bytesImage;

  @override
  void initState() {
    super.initState();
    fetchImage();
  }

  Future<void> fetchImage() async {
    final response = await http.get(Uri.parse('http://localhost:3000/image'));
    if (response.statusCode == 200) {
      final data = jsonDecode(response.body);
      setState(() {
        bytesImage = base64Decode(data['image']);
      });
    } else {
      throw Exception('Falha ao carregar a imagem');
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Teste de Imagem'),
      ),
      body: Center(
        child: bytesImage != null
            ? Image.memory(
                bytesImage!,
                errorBuilder: (context, error, stackTrace) {
                  return Icon(Icons.error);
                },
              )
            : CircularProgressIndicator(),
      ),
    );
  }
}

void main() {
  runApp(MaterialApp(
    home: ImageTest(),
  ));
}

Conclusão

Se após todos esses passos o problema persistir, pode haver um problema mais profundo, como uma incompatibilidade entre o formato dos dados ou um bug no Flutter. Nesse caso, você pode tentar usar outra biblioteca para exibir imagens, como cached_network_image, ou abrir uma issue no repositório do Flutter no GitHub.


Boa sorte, Gustavo! 🚀

ChatGPT

Oi Gustavo,

Pelo que você compartilhou, os bytes retornados no print('Primeiros bytes: ${bytesImage.take(10).toList()}') não estão no formato de uma imagem válida. Os valores [49, 55, 51, 53, 54, 52, 55, 55, 55, 48] correspondem ao texto ASCII, não a bytes reais de uma imagem. Isso indica que a API provavelmente está retornando os dados de imagem como uma string (possivelmente em formato hexadecimal ou Base64) em vez de um array de bytes binários.

Aqui estão os passos para corrigir isso:


1. Verifique o backend

Certifique-se de que o backend está retornando os dados de imagem no formato correto. No caso do PostgreSQL com BYTEA, os dados podem ser convertidos para uma string hexadecimal ou Base64 ao serem enviados pela API. Verifique se está usando algo como toBase64() ou outro método de codificação.

No Node.js, garanta que os dados sejam enviados como Buffer em formato binário:

app.get('/get-image', async (req, res) => {
  const result = await db.query('SELECT image FROM your_table WHERE id = $1', [id]);
  const imageBuffer = result.rows[0].image; // Certifique-se de que isso seja do tipo BYTEA
  res.send(imageBuffer);
});

2. Decodifique corretamente no Flutter

Se os dados estão vindo em uma string (como parece ser o caso pelos valores ASCII), você precisará primeiro convertê-los corretamente. Aqui está como ajustar no Flutter:

Se a API retorna Base64:

import 'dart:convert';
import 'dart:typed_data';
import 'package:flutter/material.dart';

Uint8List bytesImage = base64Decode(element['image']['data']);
Image image = Image.memory(
  bytesImage,
  errorBuilder: (context, error, stackTrace) {
    print('Erro ao carregar imagem: $error');
    return Icon(Icons.error);
  },
);

Se a API retorna Hexadecimal:

Use um utilitário para converter de string hexadecimal para Uint8List:

Uint8List hexToBytes(String hex) {
  return Uint8List.fromList(List.generate(
    hex.length ~/ 2,
    (i) => int.parse(hex.substring(i * 2, i * 2 + 2), radix: 16),
  ));
}

// No seu map:
Uint8List bytesImage = hexToBytes(element['image']['data']);

3. Teste o retorno da API

Use ferramentas como Postman ou cURL para inspecionar o retorno exato da API. O ideal é que você veja:

  • Array de bytes binários: (ex.: [137, 80, 78, 71, ...]) para Image.memory.
  • String Base64: (ex.: iVBORw0KGgoAAAANS...) para decodificar com base64Decode.
  • String Hexadecimal: (ex.: 89504E470D0A1A0A...) para decodificar com hexToBytes.

4. Ajuste o backend ou a API conforme necessário

Se necessário, ajuste o backend para enviar os dados como Base64 diretamente ou corrija o Flutter para interpretar o formato exato retornado.


Com isso, acredito que o problema será resolvido. 🚀

Perplexity

Analisando seu código e os erros apresentados, existem alguns problemas na manipulação da imagem:

  1. Os bytes que você está recebendo ([49, 55, 51, 53, 54, 52, 55, 55, 55, 48]) parecem ser caracteres ASCII, não bytes reais de uma imagem. Estes números correspondem a caracteres de texto, não a dados binários de imagem.

Correção Sugerida

  1. Primeiro, verifique se a imagem está sendo salva corretamente no banco de dados:
// Ao salvar a imagem
final bytes = await imageFile.readAsBytes();
final base64String = base64Encode(bytes);
// Salvar base64String no banco
  1. Para carregar a imagem:
return List.from(
  list.map((element) {
    try {
      final Uint8List bytesImage = base64Decode(element['image']['data']);
      element['image'] = Image.memory(
        bytesImage,
        errorBuilder: (context, error, stackTrace) {
          print('Erro ao carregar imagem: $error');
          return const Icon(Icons.error);
        },
      );
      return ModelQuestions.toMap(element);
    } catch (e) {
      print('Erro na conversão: $e');
      return ModelQuestions.toMap(element);
    }
  }),
);

Verificações Adicionais

  • Verifique se o campo data no banco está realmente armazenando os bytes da imagem em formato base64
  • Adicione logs para verificar o formato dos dados antes da conversão:
print('Tipo dos dados: ${element['image']['data'].runtimeType}');
print('Tamanho dos dados: ${element['image']['data'].length}');

Se o problema persistir, tente enviar a imagem como um arquivo binário puro em vez de base64, usando um campo BLOB no banco de dados.

Meu querido, muito obrigado pela atenção!!!

Vou testar tudo isso, e te retorno. Te desejo um excelente 2025, e que realmente tenhamos menos bugs, rs, mas faz parte do nosso crescimento!!!

Abraço!! ate breve!!!