Visualizzazione dei risultati da 1 a 10 su 10
  1. #1
    Utente di HTML.it L'avatar di §elva
    Registrato dal
    Nov 2006
    Messaggi
    607

    [jsp-servlet]inserimento e recupero immagine in database

    Salve, sto facendo un progetto didattico e devo inserire in un database una immagine tramite un form html di upload. Successivamente devo poter recuperare l'immagine per visualizzarla in una pagina web.
    Il salvataggio dell'immagine nel database sembrerebbe funzionare, mentre il recupero no.
    Vi mostro quanto fatto fin'ora.

    Ho scritto la seguente servlet che con la chiamata post salva nel DB l'immagine(accedendovi tramite
    un nome del prodotto e il nome del fornitore del prodotto, mentre tramite il metodo doGet restituisce
    l'immagine:

    codice:
    public class ManageImagesServlet extends AbstractDatabaseServlet {
    
    	public void doGet(HttpServletRequest request, HttpServletResponse response)
    			throws ServletException, IOException {
    		String nomeProdotto = (String) request.getParameter("nome-prodotto");
    		String nomeFornitore = (String) request.getParameter("nome-fornitore");
    		if (nomeProdotto != null && nomeFornitore != null) {
    			PrintWriter out = response.getWriter();
    			int i;
    			Connection con;
    			try {
    				con = DS.getConnection();
    
    				SaveTakeImagesDatabase dbms = new SaveTakeImagesDatabase(con);
    				InputStream is = dbms
    						.searchPicture(nomeProdotto, nomeFornitore);
    				BufferedInputStream bis = new BufferedInputStream(is);
    				response.setContentType("image/jpeg");
    				response.setContentLength(bis.available());
    
    				while ((i = bis.read()) != -1)
    					out.write(i);
    				// chiudo lo stream in lettura
    				bis.close();
    				
    			} catch (SQLException e) {
    				// TODO ...
    			}
    		}
    
    	}
    
    	public void doPost(HttpServletRequest request, HttpServletResponse response)
    			throws ServletException, IOException {
    		MultipartRequest multi = new MultipartRequest(request, "/tmp/");
    
    		// dati del form
    		String nomeProdotto = (String) multi.getParameter("nome-prodotto");
    		String nomeFornitore = (String) multi.getParameter("nome-fornitore");
    		File f = multi.getFile("image");
    		if (f == null) {
    			// TODO: comunicarlo alla JSP
    		} else {
    			// memorizzo nel DB l'immagine
    			Connection con;
    			try {
    				con = DS.getConnection();
    				SaveTakeImagesDatabase dbms = new SaveTakeImagesDatabase(con);
    				dbms.storePicture(nomeProdotto, nomeFornitore, f);
    
    				//TODO modificare il redirect
    				response.sendRedirect("../html/image-test.html");
    			} catch (SQLException e) {
    				// TODO ..
    			}
    
    		}
    	}
    
    }
    La classe SaveTakeImagesDatabase realizza i metodi per accedere al database:
    codice:
    public class SaveTakeImagesDatabase {
    
    	private final Connection con;
    
    	public SaveTakeImagesDatabase(Connection con) {
    		this.con = con;
    	}
    
    	public void storePicture(String nameP, String nameF, File f)
    			throws SQLException {
    		String insertpic = "UPDATE PPJ.Prodotto " + "SET immagine = ? "
    				+ "WHERE nome =? AND nomeFornitore =?";
    
    		PreparedStatement pst = null;
    		pst = con.prepareStatement(insertpic);
    		pst.clearParameters();
    		try {
    			pst.setBinaryStream(1, new FileInputStream(f), (int) f.length());
    		} catch (FileNotFoundException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
    		pst.setString(2, nameP);
    		pst.setString(3, nameF);
    
    		pst.execute();
    		con.close();
    	}
    
    	public InputStream searchPicture(String nomeP, String nomeF)
    			throws SQLException {
    		String getpic = "SELECT immagine " + "FROM PPJ.Prodotto "
    				+ "WHERE nome=? AND nomeFornitore=?";
    		PreparedStatement pstmt = null;
    		ResultSet rs = null;
    		InputStream is = null;
    		pstmt = con.prepareStatement(getpic);
    		pstmt.clearParameters();
    		pstmt.setString(1, nomeP);
    		pstmt.setString(2, nomeF);
    		rs = pstmt.executeQuery();
    		rs.next();
                    is = rs.getBinaryStream("picture");
    		con.close();
    		return is;
    	}
    
    }
    Il form per l'upload prende i dati e li comunica alla servlet tramite POST (../images rappresenta la servlet)
    codice:
    	<FORM METHOD="POST" ACTION="../images" ENCTYPE="multipart/form-data">
    		<INPUT TYPE="TEXT" NAME="nome-prodotto">
    		<INPUT TYPE="TEXT" NAME="nome-fornitore">
    		<INPUT TYPE="FILE" NAME="image">
    		<INPUT TYPE="SUBMIT" NAME="submit" VALUE="upload">
    	</FORM>
    mentre per visualizzare l'mmagine sul browser utilizzo (nome del prodotto: "iPhone 5 64GB", nome fornitore: "Apple" per esempio)
    codice:
    [img]../images?nome-prodotto=iPhone+5+64GB&nome-fornitore=Apple[/img]
    Grazie in anticipo
    da sempre l'ignoranza fa paura, ma il silenzio è uguale a morte

  2. #2
    Utente di HTML.it L'avatar di §elva
    Registrato dal
    Nov 2006
    Messaggi
    607
    Un errore sta nella classe SaveTakeImagesDatabase dove
    is = rs.getBinaryStream("picture");
    va sostituito con
    is = rs.getBinaryStream("immagine");

    Sto impazzendo
    da sempre l'ignoranza fa paura, ma il silenzio è uguale a morte

  3. #3
    Moderatore di Programmazione L'avatar di LeleFT
    Registrato dal
    Jun 2003
    Messaggi
    17,325
    Un altro errore è la chiusura della connessione appena dopo aver ottenuto l'InputStream.
    In pratica, chiudi la connessione prima di aver avuto modo di usare l'InputStream... e quando poi lo andrai ad usare, da dove li leggerà i dati se la connessione è già chiusa?

    La connessione al database è l'ultima cosa che va chiusa, dopo aver rilasciato tutte le risorse (quindi, dopo aver chiuso l'InputStream ottenuto, dopo aver chiuso il RecordSet ottenuto e dopo aver chiuso il PreparedStatement).

    PS. Se non vuoi "impazzire" con i nomi delle colonne, semplicemente usa gli indici. Se sai che il dato che ti serve è nella prima colonna (in effetti, fai una SELECT di un solo campo), puoi fare così:

    codice:
    // Non mi importa del nome del primo campo, dico al RecordSet di
    // prendere il "primo" campo, comunque si chiami
    is = rs.getBinaryStream( 1 );

    Ma... la domanda qual è?


    Ciao.
    "Perchè spendere anche solo 5 dollari per un S.O., quando posso averne uno gratis e spendere quei 5 dollari per 5 bottiglie di birra?" [Jon "maddog" Hall]
    Fatti non foste a viver come bruti, ma per seguir virtute e canoscenza

  4. #4
    Utente di HTML.it L'avatar di §elva
    Registrato dal
    Nov 2006
    Messaggi
    607
    Ciao, grazie della risposta! La domanda è "perchè non funziona?"
    Motificando il codice tenendo conto dei tuoi accorgimenti non cambia il risultato..

    Come database uso postgresql e come dominio per l'immagine utilizzo bytea. Questi pezzi di codice li ho presi da una guida, però leggendo in internet alcuni hanno risolto usando il dominio blob invece di bytea.
    Mi sembra strano, avendo preso la guida da un docente universitario, che dite?

    Intanto ho modificato doGet in questo modo
    codice:
    public void doGet(HttpServletRequest request, HttpServletResponse response)
    			throws ServletException, IOException {
    		String nomeProdotto = (String) request.getParameter("nome-prodotto");
    		String nomeFornitore = (String) request.getParameter("nome-fornitore");
    		if (nomeProdotto != null && nomeFornitore != null) {
    			// ottengo lo stream di output verso la JSP
    			PrintWriter out = response.getWriter();
    			int i;
    			Connection con;
    			try {
    				con = DS.getConnection();
    
    				//SaveTakeImagesDatabase dbms = new SaveTakeImagesDatabase(con);
    
    				String getpic = "SELECT immagine " + "FROM PPJ.Prodotto "
    						+ "WHERE nome=? AND nomeFornitore=?";
    				PreparedStatement pstmt = null;
    				ResultSet rs = null;
    				InputStream is = null;
    				pstmt = con.prepareStatement(getpic);
    				pstmt.clearParameters();
    				pstmt.setString(1, nomeProdotto);
    				pstmt.setString(2, nomeFornitore);
    				rs = pstmt.executeQuery();
    				rs.next();
    				// l ’immagine di tipo bytea nel DB viene ottenuta come
    				// un binary stream in particolare un InputStream
    				is = rs.getBinaryStream(1);
    
    				BufferedInputStream bis = new BufferedInputStream(is);
    				// imposto il tipo della risposta alla JSP
    				response.setContentType("image/jpeg");
    				// imposto la dimensione in byte della risposta alla JSP
    				response.setContentLength(bis.available());
    				// byte per byte copio l ’ immagine letta dal DB sullo stream
    				// verso la JSP
    				while ((i = bis.read()) != -1)
    					out.write(i);
    
    				bis.close();
    				out.flush();
    				out.close();
    				con.close();
    
    			} catch (SQLException e) {
    				// TODO in caso d'errore dove reindirizzo?
    			}
    		}
    
    	}
    da sempre l'ignoranza fa paura, ma il silenzio è uguale a morte

  5. #5
    Moderatore di Programmazione L'avatar di LeleFT
    Registrato dal
    Jun 2003
    Messaggi
    17,325
    Originariamente inviato da §elva
    Ciao, grazie della risposta! La domanda è "perchè non funziona?"
    E cosa vorrebbe dire "non funziona"?
    Ti dà risultati errati?
    Non ti dà nessun risultato?
    Hai delle eccezioni? Se sì, dovresti postarne lo stackTrace, altrimenti che ne sappiamo noi?
    Hai degli errori in compilazione? Se sì, quali?

    Cerca di essere precisa quando esponi il problema.

    PS: controlla sempre i log del tuo Servlet Container / Application Server.

    Ciao.
    "Perchè spendere anche solo 5 dollari per un S.O., quando posso averne uno gratis e spendere quei 5 dollari per 5 bottiglie di birra?" [Jon "maddog" Hall]
    Fatti non foste a viver come bruti, ma per seguir virtute e canoscenza

  6. #6
    Utente di HTML.it L'avatar di §elva
    Registrato dal
    Nov 2006
    Messaggi
    607
    Cerca di essere precisa quando esponi il problema.
    Sì, hai ragione
    Fino due giorni fa la servlet veniva caricata su tomcat correttamente e il form di upload sembrava funzionare. Infatti se poi accedevo al database tramite terminale, facendo una select sull'attributo immagine questo stampava a video delle "pagine" vuote, dovute presubilmente al modo in cui stampa il campo bytea. Ho provato anche a fare una select sul campo tramite la codifica in base64 del contenuto e in questo caso mi viene stampata a video una stringa lunghissima. Ora non so se il contenuto nel database sia corretto, ma qualcosa dentro c'è finito
    La pagina che tenta di recuperare l'immagine però non sembra funzionare a dovere: quando la apro dal browser mi compare il simbolo classico delle immagini non trovate (quel rettangolino che contiene l'icona di un file spezzato per intenderci).

    Da ieri invece mi si è aggiunto un ulteriore problema. La servlet sembra avere problemi nel caricamento su tomcat. Quando premo il pulsante di upload nel form il server risponde con il seguente errore:

    codice:
    HTTP Status 500 - Error instantiating servlet class servlet.ManageImagesServlet
    
    type Exception report
    
    message Error instantiating servlet class servlet.ManageImagesServlet
    
    description The server encountered an internal error that prevented it from fulfilling this request.
    
    exception
    
    javax.servlet.ServletException: Error instantiating servlet class servlet.ManageImagesServlet
    	org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
    	org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:99)
    	org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:936)
    	org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:407)
    	org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1004)
    	org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:589)
    	org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:312)
    	java.util.concurrent.ThreadPoolExecutor$Worker.runTask(Unknown Source)
    	java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    	java.lang.Thread.run(Unknown Source)
    
    root cause
    
    java.lang.ExceptionInInitializerError
    	sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    	sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
    	sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
    	java.lang.reflect.Constructor.newInstance(Unknown Source)
    	java.lang.Class.newInstance0(Unknown Source)
    	java.lang.Class.newInstance(Unknown Source)
    	org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
    	org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:99)
    	org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:936)
    	org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:407)
    	org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1004)
    	org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:589)
    	org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:312)
    	java.util.concurrent.ThreadPoolExecutor$Worker.runTask(Unknown Source)
    	java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    	java.lang.Thread.run(Unknown Source)
    
    root cause
    
    java.lang.IllegalStateException: Impossibile recuperare il pool di connessioni al database: FATAL: remaining connection slots are reserved for non-replication superuser connections
    	servlet.AbstractDatabaseServlet.<clinit>(AbstractDatabaseServlet.java:31)
    	sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    	sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
    	sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
    	java.lang.reflect.Constructor.newInstance(Unknown Source)
    	java.lang.Class.newInstance0(Unknown Source)
    	java.lang.Class.newInstance(Unknown Source)
    	org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
    	org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:99)
    	org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:936)
    	org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:407)
    	org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1004)
    	org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:589)
    	org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:312)
    	java.util.concurrent.ThreadPoolExecutor$Worker.runTask(Unknown Source)
    	java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    	java.lang.Thread.run(Unknown Source)
    Purtroppo non posso avere accesso ai file log nel server, in quanto questo è un server remoto.
    Grazie ancora
    da sempre l'ignoranza fa paura, ma il silenzio è uguale a morte

  7. #7
    Moderatore di Programmazione L'avatar di LeleFT
    Registrato dal
    Jun 2003
    Messaggi
    17,325
    Beh, quell'errore è molto chiaro (e scritto in italiano, anche). La servlet "servlet.ManageImageServlet" non viene proprio istanziata ed il motivo è scritto nell'ultima eccezione:

    Impossibile recuperare il pool di connessioni al database.

    Quindi, probabilmente c'è qualche configurazione sbagliata su Tomcat o sulla tua WebApplication o proprio non ci sono più slot disponibili (che potrebbe essere se vengono aperte connessioni e poi non vengono chiuse).

    Dato che il server è remoto e non ne hai accesso, sinceramente non so come poterti aiutare. Se la causa del problema è l'eccessivo numero di connessioni aperte verso il DB, un riavvio della macchina (o anche solo di Tomcat) risolverebbe il problema.

    Ciao.
    "Perchè spendere anche solo 5 dollari per un S.O., quando posso averne uno gratis e spendere quei 5 dollari per 5 bottiglie di birra?" [Jon "maddog" Hall]
    Fatti non foste a viver come bruti, ma per seguir virtute e canoscenza

  8. #8
    Utente di HTML.it L'avatar di §elva
    Registrato dal
    Nov 2006
    Messaggi
    607
    Ok, grazie, sei stato gentilissimo
    Quindi questo si può verificare utilizzando Connection senza richiamarne poi il metodo close(), giusto?
    Essendo il server in remoto l'unico modo per me è contattare i sistemisti
    Oppure le connessioni vengono abbattute se rimangono sospese per un determinato periodo di tempo?
    da sempre l'ignoranza fa paura, ma il silenzio è uguale a morte

  9. #9
    Moderatore di Programmazione L'avatar di LeleFT
    Registrato dal
    Jun 2003
    Messaggi
    17,325
    Originariamente inviato da §elva
    Quindi questo si può verificare utilizzando Connection senza richiamarne poi il metodo close(), giusto?
    Sì, se la connessione non viene chiusa/rilasciata rimane in piedi.

    Oppure le connessioni vengono abbattute se rimangono sospese per un determinato periodo di tempo?
    Dipende dalla configurazione del server e/o del DBMS.


    Ciao.
    "Perchè spendere anche solo 5 dollari per un S.O., quando posso averne uno gratis e spendere quei 5 dollari per 5 bottiglie di birra?" [Jon "maddog" Hall]
    Fatti non foste a viver come bruti, ma per seguir virtute e canoscenza

  10. #10
    Utente di HTML.it L'avatar di §elva
    Registrato dal
    Nov 2006
    Messaggi
    607
    Grazie ancora
    da sempre l'ignoranza fa paura, ma il silenzio è uguale a morte

Permessi di invio

  • Non puoi inserire discussioni
  • Non puoi inserire repliche
  • Non puoi inserire allegati
  • Non puoi modificare i tuoi messaggi
  •  
Powered by vBulletin® Version 4.2.1
Copyright © 2025 vBulletin Solutions, Inc. All rights reserved.