web-dev-qa-db-fra.com

Moyen le plus simple de servir des données statiques de l'extérieur du serveur d'applications dans une application Web Java

J'ai une application Web Java s'exécutant sur Tomcat. Je souhaite charger des images statiques qui seront affichées à la fois sur l'interface utilisateur Web et dans les fichiers PDF générés par l'application. De nouvelles images seront également ajoutées et enregistrées en les téléchargeant via l'interface utilisateur Web.

Ce n'est pas un problème de le faire en ayant les données statiques stockées dans le conteneur Web, mais les stocker et les charger de l'extérieur du conteneur Web me donne mal à la tête.

Je préférerais ne pas utiliser un serveur Web distinct comme Apache pour servir les données statiques à ce stade. Je n'aime pas non plus l'idée de stocker les images au format binaire dans une base de données. 

J'ai déjà vu des suggestions, comme par exemple que le répertoire d'images soit un lien symbolique pointant vers un répertoire situé en dehors du conteneur Web, mais cette approche fonctionnera-t-elle à la fois dans les environnements Windows et * nix? 

Certains suggèrent d'écrire un filtre ou une servlet pour gérer la diffusion d'images, mais ces suggestions ont été très vagues et de haut niveau, sans indications sur des informations plus détaillées sur la manière de procéder. 

124
Janne

J'ai déjà vu quelques suggestions, comme par exemple que le répertoire des images soit un lien symbolique pointant vers un répertoire situé en dehors du conteneur Web, mais cette approche fonctionnera-t-elle à la fois dans les environnements Windows et * nix?

Si vous respectez les règles de chemin d'accès du système de fichiers * nix (c'est-à-dire que vous utilisez exclusivement des barres obliques comme dans /path/to/files), cela fonctionnera également sous Windows sans qu'il soit nécessaire de bricoler avec les horribles concaténations de chaînes File.separator. Toutefois, il ne sera analysé que sur le même disque de travail à partir duquel cette commande a été appelée. Ainsi, si Tomcat est par exemple installé sur C:, le /path/to/files pointerait en fait sur C:\path\to\files.

Si les fichiers sont tous situés en dehors de l'application Web et que vous souhaitez que DefaultServlet de Tomcat les gère, il vous suffit dans Tomcat d'ajouter l'élément de contexte suivant à /conf/server.xml dans la balise <Host>:

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

De cette façon, ils seront accessibles via http://example.com/files/.... Vous trouverez un exemple de configuration GlassFish/Payara ici et un exemple de configuration WildFly ici .

Si vous voulez contrôler vous-même la lecture/l'écriture de fichiers, vous devez créer un Servlet qui obtiendra essentiellement un InputStream du fichier sous la forme, par exemple, de FileInputStream et l'écrivera dans le OutputStream du HttpServletResponse.

Sur la réponse, vous devez définir l'en-tête Content-Type pour que le client sache quelle application associer au fichier fourni. Et, vous devez définir l'en-tête Content-Length pour que le client puisse calculer la progression du téléchargement, sinon il sera inconnu. Et, vous devez définir l'en-tête Content-Disposition sur attachment si vous souhaitez une boîte de dialogue Enregistrer sous, sinon le client essaiera de l'afficher en ligne. Enfin, écrivez simplement le contenu du fichier dans le flux de sortie de la réponse.

Voici un exemple de base d'un tel 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());
    }

}

Lorsque mappé sur un url-pattern de par exemple /files/*, vous pouvez l'appeler par http://example.com/files/image.png. De cette façon, vous pouvez avoir plus de contrôle sur les demandes que le DefaultServlet, par exemple en fournissant une image par défaut (c'est-à-dire if (!file.exists()) file = new File("/path/to/files", "404.gif") ou à peu près). Également, utiliser request.getPathInfo() est préférable à request.getParameter() car il est plus convivial pour le référencement, sinon IE ne sélectionnera pas le nom de fichier correct lors de Save As.

Vous pouvez réutiliser la même logique pour servir des fichiers à partir d'une base de données. Remplacez simplement new FileInputStream() par ResultSet#getInputStream().

J'espère que cela t'aides.

Voir également:

151
BalusC

Vous pouvez le faire en plaçant vos images sur un chemin fixe (par exemple:/var/images ou c:\images), ajouter un paramètre dans les paramètres de votre application (représenté dans mon exemple par Settings.class) et les charger. comme ça, dans une HttpServlet de la vôtre:

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);
}

Ou si vous voulez manipuler l'image:

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());

alors le code html serait <img src="imageServlet?imageName=myimage.png" />

Bien sûr, vous devriez penser à servir différents types de contenu - "image/jpeg", basé par exemple sur l'extension du fichier. Aussi, vous devriez fournir un peu de cache.

De plus, vous pouvez utiliser cette servlet pour redimensionner la qualité de vos images, en fournissant des paramètres de largeur et de hauteur sous forme d'arguments, et en utilisant image.getScaledInstance(w, h, Image.SCALE_SMOOTH), en tenant compte des performances, bien sûr.

9
Bozho

Condition préalable: accès aux ressources statiques (images/vidéos, etc.) de l'extérieur du répertoire WEBROOT ou du disque local

Étape 1 :
Créez un dossier sous les applications Web du serveur Tomcat., Disons que le nom du dossier est myproj

Étape 2 : 
Sous myproj, créez un dossier WEB-INF. Créez-y un simple web.xml.

code sous web.xml

<web-app>
</web-app>

Structure du répertoire pour les deux étapes ci-dessus

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

Étape 3:
Créez maintenant un fichier XML avec le nom myproj.xml à l’emplacement suivant.

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

CODE dans myproj.xml:

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

Étape 4:
4 A) Créez maintenant un dossier avec le nom myproj dans le lecteur E de votre disque dur et créez un nouveau 

dossier avec le nom images et placez quelques images dans le dossier images (e:myproj\images\)

Supposons que myfoto.jpg soit placé sous e:\myproj\images\myfoto.jpg

4 B) Créez maintenant un dossier avec le nom WEB-INF dans e:\myproj\WEB-INF et créez un fichier web.xml dans le dossier WEB-INF.

Code dans web.xml

<web-app>
</web-app>

Étape 5: 
Créez maintenant un document .html avec le nom index.html et placez-le sous e:\myproj

CODE sous index.html Bienvenue sur Myproj

La structure du répertoire pour les étapes 4 et 5 ci-dessus est la suivante 

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

Étape 6: 
Maintenant démarrez le serveur Apache Tomcat

Étape 7: 
ouvrez le navigateur et tapez l'URL comme suit

http://localhost:8080/myproj    

puis u affichez le contenu fourni dans index.html

Étape 8: 
Accéder aux images sur votre disque dur local (en dehors de Webroot)

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

C'est l'histoire de mon lieu de travail:
- Nous essayons de télécharger plusieurs images et les fichiers de documents utilisent Struts 1 et Tomcat 7.x.
- Nous essayons d'écrire les fichiers téléchargés dans le système de fichiers, le nom du fichier et le chemin complet des enregistrements de la base de données.
- Nous essayons de séparer les dossiers de fichiers en dehors du répertoire d'application web. (*)

La solution ci-dessous est assez simple, efficace pour le besoin (*):

Dans le fichier META-INF/context.xml, le fichier contient le contenu suivant: (Exemple, mon application est exécutée à http://localhost:8080/ABC, mon application/projet nommé ABC) . de fichier context.xml)

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

(fonctionne avec Tomcat version 7 ou ultérieure)

Résultat: Nous avons été créés 2 alias. Par exemple, nous enregistrons les images sous: D:\images\foo.jpg.__ et visionnons le lien ou en utilisant le tag image: 

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

ou 

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

(J'utilise Netbeans 7.x, Netbeans semble créer automatiquement le fichier WEB-INF\context.xml)

5
foobarfuu

Ajouter au fichier server.xml: 

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

Activer le paramètre de liste de fichiers dir dans web.xml: 

    <init-param>
        <param-name>listings</param-name>
        <param-value>true</param-value>
    </init-param>
5
blue-sky

Si vous décidez d'envoyer à FileServlet, vous aurez également besoin de allowLinking="true" dans context.xml afin de permettre à FileServlet de parcourir les liens symboliques.

Voir http://Tomcat.Apache.org/Tomcat-6.0-doc/config/context.html

2
cherouvim

Si vous voulez travailler avec JAX-RS (par exemple, RESTEasy), essayez ceci:

@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);
        }
    };
}

en utilisant javax.ws.rs.core.Response et com.google.common.io.ByteStreams

0
electrobabe

Lisez le flux d'entrée d'un fichier et écrivez-le dans ServletOutputStream pour l'envoi de données binaires au 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));
    }
}

Renvoie directement l'URL dans l'attribut src.

<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
Yash

si quelqu'un n'est pas capable de résoudre son problème avec une réponse acceptée, alors notez les considérations suivantes:

  1. inutile de mentionner localhost:<port> avec l'attribut <img> src.
  2. assurez-vous que vous exécutez ce projet en dehors d'Eclipse, car Eclipse crée lui-même l'entrée context docBase dans son fichier server.xml local.
0
JPG