Ae galera blz ?
Eu criei um servlet que retira uma imagem do banco de dados e a cria no browser, para isso usei o objeto GifEncoder do pacote da ACME (www.acme.com). Todo o processo funciona normalmente e a imagem é mostrada sem maiores problemas.
Só que quando são feitos dois ou mais acessos simultâneos nesse servlet ele gera um erro de OutOfMemory exatamente no ponto onde é gerada a imagem, e todas as chamadas ficam presas indefinidamente até esgotar todos os recursos da máquina. Para resolver isso eu coloquei um bloco synchronized, então ficou assim:
Só que isso é um baita de um gargalo no servlet
Então aí vai a pergunta óbvia, como fazer ele gerar a imagem nos acessos simultaneos sem gerar o erro ? alguém já passou por esse problema com essa classe ou me recomenda algum outro pacote que também gere gifs ?
Rapaz… as imagens geradas são tão grandes assim? Qual servidor de aplicações você está utilizando?
Não conheco a classe que você está usando, mas antes (se você ainda não o fez) dê uma boa estudada na documentação da API, podem ser mencionadas algumas maneiras de otimizar a sua utilização.
Outra coisa, dependendo do objetivo desta imagem, pode ser interessante utilizar um pool de recursos… que organiza e gerencia as imagens utilizadas, reaproveitando-as de acordo com a demanda…
Especifique melhor qual seu objetivo, assim o pessoal pode lhe dar mais idéias alternativas, ou mesmo soluções
As imagens no banco estão no formato gif e têm 3000x2000 pixels de tamanho, elas dão em média um total de 210k cada. Estou utilizando o Tomcat 4.1.12 com o JDK1.4, por enquanto estou testando na minha máquina (AMD 500 - 512MB, é realmente o pessoal aqui precisa me dar uma máquina melhor ).
O problema é o tamanho das imagens, eu fiz uns testes com imagens menores 1000x1000px e várias requisições simultâneas não apresentaram problemas.
Alguém teve alguma experiência nisso e recomenda alguma outra solução ? Qualquer idéia, opinião é bem vinda.
Bom o que eu estou fazendo é chamando um servlet que faz uma consulta no banco SQL Server (que está no meu servidor) trazendo a imagem GIF para que ela possa ser mostrada no browser. Sim o processo é lento, devido ao tamanho na minha máquina leva pelo menos 20s para mostrar a imagem. Segue o código:
InputStream in = resultSet.getBinaryStream(“ORGANOGRAMA_IMAGEM”);
final byte[] buffer = Functions.toByteArray(in);
in.close();
ImageIcon imageIcon = new ImageIcon(buffer);
ServletOutputStream out = response.getOutputStream();
GifEncoder enc = new GifEncoder(imageIcon.getImage(), out);
synchronized (this)
{
enc.encode();
}
É só isso o que eu quero fazer, só que o erro ocorre justamente no ponto enc.encode(); quando há acesso simultâneo. Acabei postando o erro errado, não é OutOfMemory, só mostra OutOfMemory depois de mostrar este erro:
Uncaught error fetching image:
java.lang.NullPointerException
at Acme.JPM.Encoders.GifEncoder.encodeDone(GifEncoder.java:122)
at Acme.JPM.Encoders.ImageEncoder.imageComplete(ImageEncoder.java:261)
at sun.awt.image.InputStreamImageSource.errorConsumer(InputStreamImageSource.java:131)
at sun.awt.image.InputStreamImageSource.errorAllConsumers(InputStreamImageSource.java:124)
at sun.awt.image.InputStreamImageSource.doFetch(InputStreamImageSource.java:269)
at sun.awt.image.ImageFetcher.fetchloop(ImageFetcher.java:168)
at sun.awt.image.ImageFetcher.run(ImageFetcher.java:136)
Gostei da idéia do pool de recursos, você teria algum exemplo sobre isso ou recomenda algum componente para isso ?
Um problema possivel eh que os objetos que estao indo pra memoria nao estao sendo desalocados da memoria logo apos a utilizacao. Some a isso a memoria que voce esta configurando pro java (eh tomcat neh?) na hora da inicializacao da virtual machine.
Se bobear, as configuracoes de limites de memorias podem ser especificadas ADICIONALMENTE na configuracao da web application dependendo do web server.
Isto eh:
aumentar a memoria disponivel para a virtual machine do seu webserver
ver se a configuracao dele esta segurando algo mais
como voce mesmo indicou, mais acessos com imagens menores nao causam problemas, uma vez que a memoria nao estora e da tempo da memoria ser limpada antes da proxima imagem ser criada…
Primeiro de tudo. Acho que num gif cada pixel tem 1 byte. Então vc tem quase 6 Mb em cada imagem, certo?? Dá uma conferida aí…
A regra é simples: buffers são grandes. Vc não pode usar diretamente o InputStream que vc pegou do ResultSet??
Porque o bom mesmo seria que o seu encoder enviasse blocos pelo out de tempos em tempos. Do jeito que vc está fazendo, vc puxa 200k (ou 6Mb?) do resultset, aloca mais 200 pro seu byte array, e o encoder precisa de mais um pouquinho.
Fora isso, vc disse que as imagens estão no banco já no formato GIF.
Então vc pode fazer assim:
response.setContentType("iamge/gif");
InputStream in = InputStream(resultSet.getBinaryStream("ORGANOGRAMA_IMAGEM");
final byte[] buffer = new byte [1024]; // 1k
int count = in.read(buffer, 0, buffer.length);
while (count > 0) {
out.write(buffer, 0, count);
out.flush();
}
Não lembro se é assim que se seta o content type, mas é fácil de descobrir na API. Assim, o seu browser vai simplesmente mostrar o gif.
Só que nesse caso, vc não pode mandar mais nada pro usuário.
E muitas vezes, pode dar problema se vc não disse o ‘content length’ direito. Tenta sem, pq é um saco.
desculpe bater na tecla de novo mas… qual é seu objetivo com isso, as imagens realmente precisam estar armazenadas na base de dados? pergunto por que numa dessas alguém pode te sugerir uma solução alternativa
O pool de recursos pode até ser utilizado… mas ele implica que pelo menos uma imagem tenha que ficar armazenada na memória, para que possa ser reutilizada por todos os clientes… isso leva a outra pergunta, qual a demanda de imagens pelos clientes, cada cliente vizualizará apenas uma delas? varias ao mesmo tempo? uma por vez?
Na minha opinião, se você for obrigado a manter esta estrutura, acho que, com as devidas otimizações de memória, a opção do dukkejefrie pode ser uma boa
tiago o content-lenght nao eh obrigatorio no protocolo http num eh? ele soh serve pra dar uma ideia de quao grande o arquvio eh ao cliente antes de receber os dados em si, permitindo que o mesmo (cliente) tenha uma ideia de quanto % ja foi pego (barra de status do browser)
sobre manter um cache da imagem em qq lugar, eh justo o problema q ele estava (estava?) tendo, manter os dados na memoria…
Bom o problema ocorria pois a memória estourava, logo partindo da resposta do Guilherme aumentei a memória que o Tomcat reserva de 64MB para 256MB, bom agora ele aguenta até 7 requisições simultâneas
dukejeffrie gostei muito do seu método, eu testei, mas infelizmente não é possível utilizá-lo pois a imagem apesar de ser gif, não é salva nesse formato na tabela, então não é possível retorná-la sem o uso de um encoder.
Muito obrigado pela atenção de todos, me resolveram um problemão hehehe
Carlos H, o meu objetivo é colocar no sistema várias imagens de organograma que são feitas pela empresa onde trabalho, por motivos de segurança foi decidido que elas deveriam estar no banco de dados, esse banco alimenta sistemas de intranet em nossos clientes, logo só nós temos acesso a senha do banco, assim somente os usuários do sistema terão acesso as imagens. Ok eu sei que sempre existem meios para se chegar na senha do banco ou mesmo burlar a entrada na intranet, mas é sempre bom não facilitar Aliás se você conhecer alguma alternetiva melhor ficaria muito agradecido.
Ah sim Carlos H sobre o uso das imagens, são centenas (mais um motivo para manter na tabela do que controlar um bando de arquivos no diretório) e não existe entre elas as que podemos dizer “as mais usadas” e qualquer usuário tem acesso a qualquer uma, então acho que um pool fica fora de cogitação nesse caso.
Sim o content-length serve exatamente para que o Guilherme falou e não pelo menos nesse caso não havia necessidade.
uma pergunta, se a imagem nao esta em binario pura la no banco de dados, em que formato ela esta?
nao entendi o que faz esse encoder, ele transforma de que formato para gif?
Boa pergunta, só pra acrescentar essas imagens são salvas no banco através de um programa feito Delphi (são dois sistemas Java/Delphi atuando em conjunto) dentro de um campo do tipo IMAGE no SQL Server ou LONG RAW no Oracle.
Sinceramente não sei explicar o formato salvo na tabela, mas para conseguir salvar em ambos os bancos eu tenho que converter o gif em um objeto TMemoryStream do Delphi. Foi o único método que consegui no Delphi que funcionava usando gifs, pois ele se recusava a aceitar o formato do gif na tabela.
Quando eu testei o código do dukejeffrie eu consegui uma imagem no tamanho correto mas a visão que se tinha era um monte de pontos multicoloridos, parecia um arco-íris 8)
Fiquei empolgado aqui com meu pool de conexões assíncrono e acabei escrevendo meio sem olhar… : )
o content-type “iamge/gif” eu vi, mas nem sempre eu lembro que dá pra editar as msgs… : )
Karlugs, descobriu o tamanho médio de cada GIF no seu buffer??
O Carlos H também falou uma coisa certa: sempre há uma alternativa a guardar esse volume imenso de dados no banco. Aposto que um request nesse servlet demora mais de 4 segundos (sem contar o init). Nas aplicações que a gente faz aqui, 90% das vezes os dados grandes são guardados em arquivos no filesystem, e o banco mantém pathnames ou urls para eles. De repente, pode ser uma alternativa legal…
Ainda tem o lance do nio, o pacote novo de fazer buffers e tal. como vc tá usando 1.4, vc pode tentar… parece que é MUITO mais eficiente, mas não sei se no seu caso vale.
Então ele retorna no buffer o mesmo tamanho do meus arquivos (200k em média) isso segundo o retorno do método InputStream.available().
A idéia de usar pathnames/urls nas tabelas é válida mas fica o risco do pessoal de informática dos clientes entrar onde não deve . No caso do banco só nós temos a senha.
Valeu pela dica do nio, vou dar uma olhada com certeza.
E se você ao invés de manter o objeto na memória salvasse o arquivo em alguma área de swap ou coisa assim no HD? Assim uma rotina verificaria se a imagem já não está salva… se estiver, linka para o HD, caso contrario, a puxa da base… e estas imagens podem ser removidas de tempos em tempos… enfim… a idéia esta dada ;))