Il modo più semplice per fornire dati statici dall'esterno del server delle applicazioni in un'applicazione Web Java


131

Ho un'applicazione web Java in esecuzione su Tomcat. Voglio caricare immagini statiche che verranno mostrate sia nell'interfaccia utente Web sia nei file PDF generati dall'applicazione. Inoltre, nuove immagini verranno aggiunte e salvate caricando tramite l'interfaccia utente Web.

Non è un problema farlo avendo i dati statici archiviati nel contenitore Web ma archiviarli e caricarli dall'esterno del contenitore Web mi dà mal di testa.

Preferirei non usare un web server separato come Apache per servire i dati statici a questo punto. Inoltre, non mi piace l'idea di archiviare le immagini in binario in un database.

Ho visto alcuni suggerimenti come avere la directory delle immagini come un collegamento simbolico che punta a una directory esterna al contenitore Web, ma questo approccio funzionerà sia in ambiente Windows che * nix?

Alcuni suggeriscono di scrivere un filtro o un servlet per la gestione dell'immagine, ma quei suggerimenti sono stati molto vaghi e di alto livello senza puntatori a informazioni più dettagliate su come ottenere questo risultato.

Risposte:


161

Ho visto alcuni suggerimenti come avere la directory delle immagini come un collegamento simbolico che punta a una directory esterna al contenitore Web, ma questo approccio funzionerà sia in ambiente Windows che * nix?

Se segui le regole del percorso del filesystem * nix (cioè usi esclusivamente le barre dirette come in /path/to/files), funzionerà anche su Windows senza la necessità di armeggiare con brutte File.separatorconcatenazioni di stringhe. Sarebbe tuttavia scansionato solo sullo stesso disco di lavoro dal quale è stato invocato questo comando. Quindi, se Tomcat è ad esempio installato C:poi la /path/to/filessarebbe in realtà punto diC:\path\to\files .

Se i file si trovano tutti al di fuori della webapp e si desidera avere Tomcat DefaultServletper gestirli, tutto ciò che è praticamente necessario fare in Tomcat è aggiungere il seguente elemento Context al tag /conf/server.xmlinterno <Host>:

<Context docBase="/path/to/files" path="/files" />

In questo modo saranno accessibili attraverso http://example.com/files/.... L'esempio di configurazione GlassFish / Payara può essere trovato qui e l'esempio di configurazione WildFly può essere trovato qui .

Se vuoi avere il controllo sulla lettura / scrittura dei file da solo, allora devi creare un Servletper questo che fondamentalmente ottiene uno InputStreamdei file nel sapore di per esempio FileInputStreame lo scrive OutputStreamnelHttpServletResponse .

Sulla risposta, è necessario impostare l' Content-Typeintestazione in modo che il client sappia quale applicazione associare al file fornito. E, dovresti impostare l' Content-Lengthintestazione in modo che il client possa calcolare l'avanzamento del download, altrimenti sarà sconosciuto. E, dovresti impostare l' Content-Dispositionintestazione su attachmentse vuoi un Salva con nome finestra di dialogo , altrimenti il ​​client tenterà di visualizzarla in linea. Infine, basta scrivere il contenuto del file nel flusso di output della risposta.

Ecco un esempio di base di tale servlet:

@WebServlet("/files/*")
public class FileServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException
    {
        String filename = URLDecoder.decode(request.getPathInfo().substring(1), "UTF-8");
        File file = new File("/path/to/files", filename);
        response.setHeader("Content-Type", getServletContext().getMimeType(filename));
        response.setHeader("Content-Length", String.valueOf(file.length()));
        response.setHeader("Content-Disposition", "inline; filename=\"" + file.getName() + "\"");
        Files.copy(file.toPath(), response.getOutputStream());
    }

}

Se mappato su un url-patterndi per esempio /files/*, puoi chiamarlo per http://example.com/files/image.png. In questo modo puoi avere un maggiore controllo sulle richieste rispetto alle richieste DefaultServlet, ad esempio fornendo un'immagine predefinita (ovvero if (!file.exists()) file = new File("/path/to/files", "404.gif")circa). Anche l'utilizzo di request.getPathInfo()è preferito sopra request.getParameter()perché è più SEO friendly e altrimenti IE non sceglierà il nome file corretto durante il Salva con nome .

Puoi riutilizzare la stessa logica per pubblicare file dal database. Sostituisci semplicemente new FileInputStream()con ResultSet#getInputStream().

Spero che questo ti aiuti.

Guarda anche:


1
@SalutonMondo: la strada con il minimo sforzo.
BalusC,

@ BalusC, ho provato questo: <Context docBase="/path/to/images" path="/images" />in Windows, ma ottenendo il percorso relativo alla cartella webapps:C:\install\apache-tomcat-8.0.26\webapps\tmp] is not valid
ACV

Su Windows dovrebbe essere:<Context docBase="C:\tmp\" path="/images" />
ACV,

E ottenere HTTP Status 404 - /images/quando si fa http://localhost:8080/images/:, MA un file specifico da under tmpworks: http://localhost:8080/images/Tulips.jpgva bene
ACV

In questo modo se inserisco un'immagine all'interno della cartella, il collegamento all'immagine darà la risposta 404 a meno che non riavvii il server, c'è qualche motivo per questo?
Mateus Viccari

9

Puoi farlo inserendo le tue immagini su un percorso fisso (ad esempio: / var / images o c: \ images), aggiungi un'impostazione nelle impostazioni dell'applicazione (rappresentata nel mio esempio da Settings.class) e caricale così, in uno HttpServletdei tuoi:

String filename = Settings.getValue("images.path") + request.getParameter("imageName")
FileInputStream fis = new FileInputStream(filename);

int b = 0;
while ((b = fis.read()) != -1) {
        response.getOutputStream().write(b);
}

O se vuoi manipolare l'immagine:

String filename = Settings.getValue("images.path") + request.getParameter("imageName")
File imageFile = new File(filename);
BufferedImage image = ImageIO.read(imageFile);
ImageIO.write(image, "image/png", response.getOutputStream());

allora il codice html sarebbe <img src="imageServlet?imageName=myimage.png" />

Ovviamente dovresti pensare a servire diversi tipi di contenuto - "image / jpeg", ad esempio in base all'estensione del file. Inoltre dovresti fornire un po 'di cache.

Inoltre, è possibile utilizzare questo servlet per il ridimensionamento di qualità delle immagini, fornendo parametri di larghezza e altezza come argomenti e utilizzando image.getScaledInstance(w, h, Image.SCALE_SMOOTH), considerando ovviamente le prestazioni.


2
Non hai davvero bisogno dell'API 2D Java per questo, aggiungerebbe inutilmente più sovraccarico. Basta leggere un InputStream e scrivere su OutputStream.
BalusC,

1
Sì, ho iniziato la risposta con l'idea del riscalaggio e di altre manipolazioni, ma alla fine l'ho semplificata.
Bozho,

7

Aggiungi a server.xml:

 <Context docBase="c:/dirtoshare" path="/dir" />

Abilita il parametro di elenco dei file dir in web.xml:

    <init-param>
        <param-name>listings</param-name>
        <param-value>true</param-value>
    </init-param>

Con la modifica di web.xml posso ottenere un elenco di file per inviarlo alla casella di selezione?
Tomasz Waszczyk,

1
questo cambiamento sarà nel web.xml di tomcat, non nella tua applicazione
vsingh

Grazie per questo! È necessario abilitare il parametro di elenco dei file dir in web.xml?
Dariru,

6

Requisito: accesso alle risorse statiche (immagini / video, ecc.) Dall'esterno della directory WEBROOT o dal disco locale

Passaggio 1:
crea una cartella sotto webapps del server tomcat. Supponiamo che il nome della cartella sia myproj

Passo 2:
Sotto myproj crea una cartella WEB-INF sotto questa crea un semplice web.xml

codice in web.xml

<web-app>
</web-app>

Struttura della directory per i due passaggi precedenti

c:\programfile\apachesoftwarefoundation\tomcat\...\webapps
                                                            |
                                                            |---myproj
                                                            |   |
                                                            |   |---WEB-INF
                                                                |   |
                                                                    |---web.xml

Passaggio 3:
ora crea un file XML con il nome myproj.xml nella seguente posizione

c:\programfile\apachesoftwarefoundation\tomcat\conf\catalina\localhost

CODICE in myproj.xml:

<Context path="/myproj/images" docBase="e:/myproj/" crossContext="false" debug="0" reloadable="true" privileged="true" /> 

Passaggio 4:
4 A) Ora crea una cartella con nome myproj nell'unità E del tuo disco rigido e creane una nuova

cartella con le immagini dei nomi e posizionare alcune immagini nella cartella delle immagini (e:myproj\images\)

Supponiamo che myfoto.jpg sia posizionato sotto e:\myproj\images\myfoto.jpg

4 B) Ora crea una cartella con il nome WEB-INF e:\myproj\WEB-INFe crea un web.xml nella cartella WEB-INF

Codice in web.xml

<web-app>
</web-app>

Passaggio 5:
ora crea un documento .html con il nome index.html e posizionalo sotto e: \ myproj

CODICE sotto index.html Benvenuto in Myproj

La struttura delle directory per i precedenti passaggi 4 e 5 è la seguente

E:\myproj
    |--index.html
    |
    |--images
    |     |----myfoto.jpg
    |
    |--WEB-INF
    |     |--web.xml

Passaggio 6:
ora avvia il server Apache Tomcat

Passaggio 7:
apri il browser e digita l'URL come segue

http://localhost:8080/myproj    

quindi visualizzi il contenuto fornito in index.html

Passaggio 8:
accedere alle immagini sul disco rigido locale (al di fuori di webroot)

http://localhost:8080/myproj/images/myfoto.jpg

puoi per favore suggerirmi, come fare la stessa cosa per i valori dinamici. Voglio dire, voglio scrivere i dati (xml) nella mia directory locale ee leggerlo nella mia pagina jsp. Esiste un modo per scrivere nel server gestito nella directory in modo da accedervi usando la procedura sopra ??
Sudip7,

anche se posso eseguire correttamente il file index.html ma non le immagini vengono visualizzate nel browser web
rogerwar,

Il mio post di errore sta funzionando bene Ho appena dimenticato di mettere / alla fine di E: / myproj Lo cambio in E: / myproj / e funziona bene Grazie @sbabamca
rogerwar

Ciao, grazie per il post ed è molto utile. Qui desidero caricare i file tramite l'interfaccia in quella directory specifica. Vorrei abilitare il metodo POST per lo stesso. Qualcuno può aiutarmi per favore sullo stesso.
vicky,

6

Questa è la storia dal mio posto di lavoro:
- Cerchiamo di caricare immagini multiple e file di documenti utilizzando Struts 1 e Tomcat 7.x.
- Proviamo a scrivere i file caricati nel file system, nel nome file e nel percorso completo dei record del database.
- Cerchiamo di separare le cartelle di file all'esterno della directory dell'app Web . (*)

La soluzione di seguito è piuttosto semplice, efficace per il requisito (*):

Nel file META-INF/context.xmlfile con il seguente contenuto: (Esempio, la mia applicazione viene eseguita su http://localhost:8080/ABC, la mia applicazione / progetto denominato ABC). (questo è anche il contenuto completo del file context.xml)

<?xml version="1.0" encoding="UTF-8"?>
<Context path="/ABC" aliases="/images=D:\images,/docs=D:\docs"/>

(funziona con Tomcat versione 7 o successive)

Risultato: sono stati creati 2 alias. Ad esempio, salviamo le immagini su: D:\images\foo.jpg e visualizziamo dal collegamento o usando il tag immagine:

<img src="http://localhost:8080/ABC/images/foo.jsp" alt="Foo" height="142" width="142">

o

<img src="/images/foo.jsp" alt="Foo" height="142" width="142">

(Uso Netbeans 7.x, Netbeans sembra creare automaticamente il file WEB-INF\context.xml)



0

se qualcuno non è in grado di risolvere il suo problema con una risposta accettata, prendere nota delle seguenti considerazioni:

  1. non c'è bisogno di menzionare localhost:<port>con l' <img> srcattributo.
  2. assicurati di eseguire questo progetto al di fuori di eclipse, perché eclipse crea la context docBasevoce da sola all'interno del suo server.xmlfile locale .

0

Leggere InputStream di un file e scriverlo ServletOutputStreamper l'invio di dati binari al client.

@WebServlet("/files/URLStream")
public class URLStream extends HttpServlet {
    private static final long serialVersionUID = 1L;

    public URLStream() {
        super();
    }

    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        File source = new File("D:\\SVN_Commit.PNG");
        long start = System.nanoTime();

        InputStream image = new FileInputStream(source);

        /*String fileID = request.getParameter("id");
        System.out.println("Requested File ID : "+fileID);
        // Mongo DB GridFS - https://stackoverflow.com/a/33544285/5081877
        image = outputImageFile.getInputStream();*/

        if( image != null ) {
            BufferedInputStream bin = null;
            BufferedOutputStream bout = null;
            ServletOutputStream sos = response.getOutputStream();
            try {
                bin = new BufferedInputStream( image );
                bout = new BufferedOutputStream( sos );
                int ch =0; ;
                while((ch=bin.read())!=-1) {
                    bout.write(ch);
                }
            } finally {
                bin.close();
                image.close();
                bout.close();
                sos.close();
            }

        } else {
            PrintWriter writer = response.getWriter();
            writer.append("Something went wrong with your request.");
            System.out.println("Image not available.");
        }
        System.out.println("Time taken by Stream Copy = "+(System.nanoTime()-start));
    }
}

Risultato l'URL direttamente srcall'attobute.

<img src='http://172.0.0.1:8080/ServletApp/files/URLStream?id=5a575be200c117cc2500003b' alt="mongodb File"/>
<img src='http://172.0.0.1:8080/ServletApp/files/URLStream' alt="local file"/>

<video controls="controls" src="http://172.0.0.1:8080/ServletApp/files/URLStream"></video>

0

Se vuoi lavorare con JAX-RS (es. RESTEasy) prova questo:

@Path("/pic")
public Response get(@QueryParam("url") final String url) {
    String picUrl = URLDecoder.decode(url, "UTF-8");

    return Response.ok(sendPicAsStream(picUrl))
            .header(HttpHeaders.CONTENT_TYPE, "image/jpg")
            .build();
}

private StreamingOutput sendPicAsStream(String picUrl) {
    return output -> {
        try (InputStream is = (new URL(picUrl)).openStream()) {
            ByteStreams.copy(is, output);
        }
    };
}

usando javax.ws.rs.core.Responseecom.google.common.io.ByteStreams


-1

L'ho fatto ancora più semplice. Problema: un file CSS aveva collegamenti URL alla cartella img. Ottiene 404.

Ho guardato url, http: // tomcatfolder: port / img / blablah.png , che non esiste. Ma questo sta davvero puntando all'app ROOT in Tomcat.

Quindi ho appena copiato la cartella img dalla mia webapp in quell'app ROOT. Lavori!

Non raccomandato per la produzione, ovviamente, ma questo è per un'app di sviluppo strumento interna.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.