Le moyen le plus simple de diffuser des données statiques depuis l'extérieur du serveur d'applications dans une application Web Java

131

J'ai une application Web Java fonctionnant 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 par téléchargement via l'interface utilisateur Web.

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

Je préfère 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 en binaire dans une base de données.

J'ai vu des suggestions telles que le fait que le répertoire d'image soit un lien symbolique pointant vers un répertoire en dehors du conteneur Web, mais cette approche fonctionnera-t-elle à la fois sur les environnements Windows et * nix?

Certains suggèrent d'écrire un filtre ou un servlet pour gérer la diffusion d'image, mais ces suggestions ont été très vagues et de haut niveau sans pointeurs vers des informations plus détaillées sur la façon d'accomplir cela.

Janne
la source

Réponses:

161

J'ai vu des suggestions telles que le fait que le répertoire d'image soit un lien symbolique pointant vers un répertoire en dehors du conteneur Web, mais cette approche fonctionnera-t-elle à la fois sur les environnements Windows et * nix?

Si vous respectez les règles de chemin du système de fichiers * nix (c'est-à-dire que vous utilisez exclusivement des barres obliques comme dans /path/to/files), alors cela fonctionnera également sous Windows sans avoir besoin de bricoler avec File.separatordes concaténations de chaînes laides . Il ne serait cependant analysé que sur le même disque de travail que celui d'où cette commande a été invoquée. Donc, si Tomcat est par exemple installé sur, C:alors le /path/to/filespointerait vers C:\path\to\files.

Si les fichiers sont tous situés à l'extérieur de l'application Web et que vous souhaitez que Tomcat les DefaultServletgère, tout ce que vous devez faire dans Tomcat est d'ajouter l'élément de contexte suivant /conf/server.xmlà la <Host>balise interne :

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

De cette façon, ils seront accessibles via http://example.com/files/.... Un exemple de configuration GlassFish / Payara peut être trouvé ici et un exemple de configuration WildFly peut être trouvé ici .

Si vous voulez avoir le contrôle de lecture / écriture de fichiers vous, vous devez créer un Servletpour ce qui obtient fondamentalement juste un InputStreamdu fichier dans la saveur de par exemple FileInputStreamet écrit au OutputStreamdu HttpServletResponse.

Sur la réponse, vous devez définir l'en- Content-Typetête afin que le client sache quelle application associer au fichier fourni. Et, vous devez définir l'en- Content-Lengthtête pour que le client puisse calculer la progression du téléchargement, sinon il sera inconnu. Et, vous devez définir l'en- Content-Dispositiontête sur attachmentsi vous voulez une boîte de dialogue Enregistrer sous , sinon le client tentera 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());
    }

}

Lorsqu'il est mappé sur un url-patternde 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 DefaultServletfait, comme la fourniture d'une image par défaut (c'est if (!file.exists()) file = new File("/path/to/files", "404.gif")-à- dire environ). Il request.getPathInfo()est également préférable d' utiliser le ci request.getParameter()- dessus car il est plus convivial pour le référencement et sinon IE ne choisira pas le nom de fichier correct lors de l' enregistrement sous .

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

J'espère que cela t'aides.

Voir également:

BalusC
la source
1
@SalutonMondo: la voie avec le moins d'effort.
BalusC
@BalusC, j'ai essayé ceci: <Context docBase="/path/to/images" path="/images" />sous Windows, mais obtenant le chemin relatif au dossier webapps:C:\install\apache-tomcat-8.0.26\webapps\tmp] is not valid
ACV
Sous Windows, il devrait être:<Context docBase="C:\tmp\" path="/images" />
ACV
Et obtenir HTTP Status 404 - /images/en faisant http://localhost:8080/images/:, MAIS un fichier spécifique sous tmpfonctionne: http://localhost:8080/images/Tulips.jpgest OK
ACV
De cette façon, si je mets une image dans le dossier, le lien vers l'image donnera la réponse 404 sauf si vous redémarrez le serveur, y a-t-il une raison à cela?
Mateus Viccari
9

Vous pouvez le faire en mettant 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 le Settings.class), et les charger comme ça, dans l'un HttpServletdes vôtres:

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 souhaitez 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", par exemple en fonction de l'extension de fichier. Vous devez également fournir une mise en cache.

De plus, vous pouvez utiliser ce servlet pour un redimensionnement de qualité de vos images, en fournissant des paramètres de largeur et de hauteur comme arguments, et en utilisant image.getScaledInstance(w, h, Image.SCALE_SMOOTH), en tenant compte des performances, bien sûr.

Bozho
la source
2
Vous n'avez vraiment pas besoin de l'API Java 2D pour cela, cela ne ferait qu'ajouter inutilement plus de frais généraux. Il suffit de lire un InputStream et d'écrire dans OutputStream.
BalusC
1
Oui, j'ai commencé la réponse avec l'idée de redimensionner et d'autres manipulations, mais j'ai fini par la simplifier.
Bozho
7

Ajouter à server.xml:

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

Activez 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>
ciel bleu
la source
Avec le changement de web.xml, je peux obtenir une liste de fichiers afin de l'envoyer à la boîte de sélection?
Tomasz Waszczyk
1
ce changement sera dans le web.xml de tomcat, pas dans votre application
vsingh
Merci pour cela! L'activation du paramètre de liste de fichiers dir dans web.xml est-elle nécessaire?
Dariru
6

Condition: accéder aux ressources statiques (images / vidéos., Etc.) depuis l'extérieur du répertoire WEBROOT ou depuis le 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 sous celui-ci, créez un simple fichier web.xml

code sous web.xml

<web-app>
</web-app>

Structure de 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 sous 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 des images et placez certaines 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 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 de 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:
Démarrez maintenant le serveur apache tomcat

Étape 7:
ouvrez le navigateur et saisissez l'url comme suit

http://localhost:8080/myproj    

puis vous affichez le contenu fourni dans index.html

Étape 8:
Pour accéder aux images sous votre disque dur local (en dehors de webroot)

http://localhost:8080/myproj/images/myfoto.jpg
sbabamca
la source
pouvez-vous s'il vous plaît me suggérer, comment faire la même chose pour les valeurs dynamiques. Je veux dire que je veux écrire les données (xml) dans mon répertoire local ou et et les lire dans ma page jsp. Existe-t-il un moyen d'écrire dans le serveur géré dans le répertoire afin que j'y accède en utilisant la procédure ci-dessus?
Sudip7 du
bien que je puisse exécuter correctement le fichier index.html mais que les images ne s'affichent pas dans le navigateur Web
rogerwar
Mon message d'erreur fonctionne bien J'ai juste oublié de mettre / à la fin de E: / myproj Je change cela en E: / myproj / et ça marche bien Merci @sbabamca
rogerwar
Salut, Merci pour le message et c'est très utile. Ici, je souhaite télécharger des fichiers via l'interface vers ce répertoire spécifique. Je souhaite activer la méthode POST pour la même chose. Quelqu'un peut-il s'il vous plaît m'aider sur le même.
vicky
6

Voici 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 de 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 de l'application Web . (*)

La solution ci-dessous est assez simple, efficace pour l'exigence (*):

Dans un META-INF/context.xmlfichier avec le contenu suivant: (Exemple, mon application s'exécute à http://localhost:8080/ABC, mon application / projet nommé ABC). (c'est aussi le contenu complet du 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 créé 2 alias. Par exemple, nous enregistrons les images sur: D:\images\foo.jpg et les visualisons à partir d'un lien ou en utilisant une balise d'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 un fichier automatiquement WEB-INF\context.xml)

Do Nhu Vy
la source
0

si quelqu'un n'est pas en mesure de résoudre son problème avec une réponse acceptée, notez les considérations ci-dessous:

  1. pas besoin de mentionner localhost:<port>avec <img> srcattribut.
  2. assurez-vous que vous exécutez ce projet en dehors de eclipse, car eclipse crée une context docBaseentrée seule dans son server.xmlfichier local .
JPG
la source
0

Lisez le InputStream d'un fichier et écrivez-le ServletOutputStreampour envoyer des 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));
    }
}

Résultat l'URL directement à l' srcattibute.

<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>
Yash
la source
0

Si vous souhaitez 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.Responseetcom.google.common.io.ByteStreams

électrobabe
la source
-1

Je l'ai fait encore plus simple. Problème: un fichier CSS contenait des liens URL vers le dossier img. Obtient 404.

J'ai regardé l'url, http: // tomcatfolder: port / img / blablah.png , qui n'existe pas. Mais cela pointe vraiment vers l'application ROOT dans Tomcat.

Je viens donc de copier le dossier img de mon application Web dans cette application ROOT. Travaux!

Non recommandé pour la production, bien sûr, mais c'est pour une application de développement d'outils interne.

Josef.B
la source