Einfachste Möglichkeit, statische Daten von außerhalb des Anwendungsservers in einer Java-Webanwendung bereitzustellen

131

Ich habe eine Java-Webanwendung, die auf Tomcat ausgeführt wird. Ich möchte statische Bilder laden, die sowohl auf der Web-Benutzeroberfläche als auch in von der Anwendung generierten PDF-Dateien angezeigt werden. Außerdem werden neue Bilder hinzugefügt und gespeichert, indem sie über die Web-Benutzeroberfläche hochgeladen werden.

Es ist kein Problem, die statischen Daten im Webcontainer zu speichern, aber das Speichern und Laden von außerhalb des Webcontainers bereitet mir Kopfschmerzen.

Ich würde es vorziehen, an dieser Stelle keinen separaten Webserver wie Apache für die Bereitstellung der statischen Daten zu verwenden. Ich mag auch nicht die Idee, die Bilder binär in einer Datenbank zu speichern.

Ich habe einige Vorschläge gesehen, wie beispielsweise, dass das Image-Verzeichnis ein symbolischer Link ist, der auf ein Verzeichnis außerhalb des Webcontainers verweist. Funktioniert dieser Ansatz jedoch sowohl in Windows- als auch in * nix-Umgebungen?

Einige schlagen vor, einen Filter oder ein Servlet für die Handhabung der Bildbereitstellung zu schreiben, aber diese Vorschläge waren sehr vage und auf hoher Ebene, ohne auf detailliertere Informationen zu verweisen, wie dies erreicht werden kann.

Janne
quelle

Antworten:

161

Ich habe einige Vorschläge gesehen, wie beispielsweise, dass das Bildverzeichnis ein symbolischer Link ist, der auf ein Verzeichnis außerhalb des Webcontainers verweist. Funktioniert dieser Ansatz jedoch sowohl in Windows- als auch in * nix-Umgebungen?

Wenn Sie die Pfadregeln für das * nix-Dateisystem einhalten (dh Sie verwenden ausschließlich Schrägstriche wie in /path/to/files), funktioniert dies auch unter Windows, ohne dass Sie mit hässlichen File.separatorZeichenfolgenverkettungen herumspielen müssen. Es wird jedoch nur auf derselben Arbeitsplatte gescannt, von der aus dieser Befehl aufgerufen wird. Wenn also beispielsweise Tomcat installiert C:ist, /path/to/fileswürde das tatsächlich darauf verweisen C:\path\to\files.

Wenn sich die Dateien alle außerhalb der Webanwendung befinden und Sie möchten, dass Tomcat DefaultServletsie verarbeitet, müssen Sie in Tomcat grundsätzlich das folgende Context-Element zum /conf/server.xmlinternen <Host>Tag hinzufügen :

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

Auf diese Weise sind sie über zugänglich http://example.com/files/.... Das GlassFish / Payara-Konfigurationsbeispiel finden Sie hier und das WildFly-Konfigurationsbeispiel finden Sie hier .

Wenn Sie die Kontrolle haben wollen , über das Lesen / Schreiben von Dateien selbst, dann müssen Sie ein schaffen Servletfür diese , die im Grunde nur eine bekommt InputStreamder Datei im Geschmack von beispielsweise FileInputStreamund schreibt sie in die OutputStreamvon derHttpServletResponse .

In der Antwort sollten Sie den Content-TypeHeader so festlegen , dass der Client weiß, welche Anwendung mit der bereitgestellten Datei verknüpft werden soll. Außerdem sollten Sie den Content-LengthHeader so einstellen , dass der Client den Download-Fortschritt berechnen kann, da er sonst nicht bekannt ist. Und Sie sollten den Content-DispositionHeader auf setzen, attachmentwenn Sie Speichern unter möchten Dialogfeld , da der Client sonst versucht, ihn inline anzuzeigen. Zum Schluss schreiben Sie einfach den Dateiinhalt in den Antwortausgabestream.

Hier ist ein grundlegendes Beispiel für ein solches 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());
    }

}

Wenn Sie url-patternbeispielsweise einem von zugeordnet sind /files/*, können Sie es von aufrufen http://example.com/files/image.png. Auf diese Weise können Sie mehr Kontrolle über die Anforderungen haben als über die Anforderungen DefaultServlet, z. B. die Bereitstellung eines Standardabbilds (dh if (!file.exists()) file = new File("/path/to/files", "404.gif")oder so). Auch die Verwendung von request.getPathInfo()wird oben bevorzugt, request.getParameter()da es SEO-freundlicher ist und der IE sonst beim Speichern unter nicht den richtigen Dateinamen auswählt .

Sie können dieselbe Logik zum Bereitstellen von Dateien aus der Datenbank wiederverwenden. Ersetzen Sie einfach new FileInputStream()durch ResultSet#getInputStream().

Hoffe das hilft.

Siehe auch:

BalusC
quelle
1
@ SalutonMondo: der Weg mit geringstem Aufwand.
BalusC
@ BalusC, ich habe dies versucht: <Context docBase="/path/to/images" path="/images" />in Windows, aber Pfad relativ zum Webapps-Ordner erhalten:C:\install\apache-tomcat-8.0.26\webapps\tmp] is not valid
ACV
Unter Windows sollte es sein:<Context docBase="C:\tmp\" path="/images" />
ACV
Und bekommen, HTTP Status 404 - /images/wenn http://localhost:8080/images/tmphttp://localhost:8080/images/Tulips.jpg
ACV
Auf diese Weise gibt der Link zum Bild die Antwort 404, wenn ich ein Bild in den Ordner lege, es sei denn, Sie starten den Server neu. Gibt es einen Grund dafür?
Mateus Viccari
9

Sie können dies tun, indem Sie Ihre Bilder auf einen festen Pfad setzen (zum Beispiel: / var / images oder c: \ images), eine Einstellung in Ihren Anwendungseinstellungen hinzufügen (in meinem Beispiel durch die Settings.class dargestellt) und sie laden so, in einem HttpServletvon Ihnen:

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

Oder wenn Sie das Bild bearbeiten möchten:

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

dann wäre der HTML-Code <img src="imageServlet?imageName=myimage.png" />

Natürlich sollten Sie daran denken, verschiedene Inhaltstypen bereitzustellen - "image / jpeg", zum Beispiel basierend auf der Dateierweiterung. Außerdem sollten Sie etwas Caching bereitstellen.

Darüber hinaus können Sie dieses Servlet für die Qualitätsskalierung Ihrer Bilder verwenden, indem Sie Breiten- und Höhenparameter als Argumente angeben und unter image.getScaledInstance(w, h, Image.SCALE_SMOOTHBerücksichtigung der Leistung verwenden.

Bozho
quelle
2
Sie brauchen die Java 2D-API dafür wirklich nicht, sie würde nur unnötig mehr Overhead hinzufügen. Lesen Sie einfach einen InputStream und schreiben Sie in OutputStream.
BalusC
1
Ja, ich habe die Antwort mit der Idee einer Neuskalierung und anderer Manipulationen begonnen, sie aber schließlich vereinfacht.
Bozho
7

Zur server.xml hinzufügen:

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

Aktivieren Sie den Parameter für die Auflistung der Verzeichnisdateien in web.xml:

    <init-param>
        <param-name>listings</param-name>
        <param-value>true</param-value>
    </init-param>
blauer Himmel
quelle
Mit der Änderung in web.xml kann ich eine Liste von Dateien erhalten, um sie an das Auswahlfeld zu senden?
Tomasz Waszczyk
1
Diese Änderung wird in
Tomcats
Danke dafür! Ist es notwendig, den Parameter für die Auflistung der Verzeichnisdateien in web.xml zu aktivieren?
Dariru
6

Voraussetzung: Zugriff auf die statischen Ressourcen (Bilder / Videos usw.) von außerhalb des WEBROOT-Verzeichnisses oder von der lokalen Festplatte

Schritt 1:
Erstellen Sie einen Ordner unter den Webanwendungen des Tomcat-Servers. Angenommen, der Ordnername lautet myproj

Schritt 2:
Erstellen Sie unter myproj einen WEB-INF-Ordner, unter dem Sie eine einfache web.xml erstellen

Code unter web.xml

<web-app>
</web-app>

Verzeichnisstruktur für die beiden oben genannten Schritte

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

Schritt 3:
Erstellen Sie nun eine XML-Datei mit dem Namen myproj.xml unter dem folgenden Speicherort

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

CODE in myproj.xml:

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

Schritt 4:
4 A) Erstellen Sie nun einen Ordner mit dem Namen myproj im Laufwerk E Ihrer Festplatte und erstellen Sie einen neuen

Ordner mit Namensbildern und legen Sie einige Bilder im Bilderordner ab (e:myproj\images\)

Nehmen wir an, myfoto.jpg steht unter e:\myproj\images\myfoto.jpg

4 B) Erstellen Sie nun einen Ordner mit dem Namen WEB-INF in e:\myproj\WEB-INF und erstellen Sie eine web.xml im Ordner WEB-INF

Code in web.xml

<web-app>
</web-app>

Schritt 5:
Erstellen Sie nun ein HTML-Dokument mit dem Namen index.html und platzieren Sie es unter e: \ myproj

CODE unter index.html Willkommen bei Myproj

Die Verzeichnisstruktur für die obigen Schritte 4 und 5 ist wie folgt

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

Schritt 6:
Starten Sie nun den Apache Tomcat Server

Schritt 7:
Öffnen Sie den Browser und geben Sie die URL wie folgt ein

http://localhost:8080/myproj    

Dann zeigen Sie den Inhalt an, der in index.html bereitgestellt wird

Schritt 8:
Zugriff auf die Bilder unter Ihrer lokalen Festplatte (außerhalb von Webroot)

http://localhost:8080/myproj/images/myfoto.jpg
sbabamca
quelle
Können Sie mir bitte vorschlagen, wie Sie dasselbe für dynamische Werte tun können? Ich meine, ich möchte die Daten (xml) in mein lokales Verzeichnis schreiben oder und und das auf meiner JSP-Seite lesen. Gibt es eine Möglichkeit, in ein Server-geschriebenes Verzeichnis zu schreiben, damit ich mit dem oben beschriebenen Verfahren darauf zugreifen kann?
Sudip7
Ich kann zwar die Datei index.html ordnungsgemäß ausführen, aber es werden keine Bilder im Webbrowser angezeigt
Rogerwar
Mein Fehlerbeitrag funktioniert gut Ich habe nur vergessen, / am Ende von E: / myproj zu setzen. Ich ändere dies in E: / myproj / und es funktioniert gut. Danke @sbabamca
rogerwar
Hallo, danke für den Beitrag und es ist sehr nützlich. Hier möchte ich Dateien über die Schnittstelle in dieses bestimmte Verzeichnis hochladen. Ich möchte die POST-Methode für dasselbe aktivieren. Kann mir bitte jemand gleich helfen.
Vicky
6

Dies ist eine Geschichte von meinem Arbeitsplatz:
- Wir versuchen, mit Struts 1 und Tomcat 7.x mehrere Bilder und Dokumentdateien hochzuladen.
- Wir versuchen, hochgeladene Dateien in das Dateisystem, den Dateinamen und den vollständigen Pfad zu den Datenbankeinträgen zu schreiben.
- Wir versuchen, Dateiordner außerhalb des Web-App-Verzeichnisses zu trennen . (*)

Die folgende Lösung ist ziemlich einfach und effektiv für die Anforderung (*):

In einer Datei META-INF/context.xmlmit folgendem Inhalt: (Beispiel: Meine Anwendung wird ausgeführt unter http://localhost:8080/ABC, meine Anwendung / mein Projekt heißt ABC). (Dies ist auch der vollständige Inhalt der Datei context.xml)

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

(funktioniert mit Tomcat Version 7 oder höher)

Ergebnis: Wir haben 2 Alias ​​erstellt. Zum Beispiel speichern wir Bilder unter: D:\images\foo.jpg und zeigen sie über einen Link an oder verwenden das Bild-Tag:

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

oder

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

(Ich benutze Netbeans 7.x, Netbeans scheinen automatisch eine Datei zu erstellen WEB-INF\context.xml)

Mach Nhu Vy
quelle
0

Wenn jemand sein Problem mit einer akzeptierten Antwort nicht lösen kann, beachten Sie die folgenden Überlegungen:

  1. Keine Notwendigkeit, localhost:<port>mit <img> srcAttribut zu erwähnen .
  2. Stellen Sie sicher, dass Sie dieses Projekt außerhalb von Eclipse ausführen, da Eclipse context docBaseeinen eigenen Eintrag in der lokalen server.xmlDatei erstellt.
JPG
quelle
0

Lesen Sie den InputStream einer Datei und schreiben Sie ihn in, ServletOutputStreamum Binärdaten an den Client zu senden.

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

srcFühren Sie die URL direkt zum Attibute.

<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
quelle
0

Wenn Sie mit JAX-RS (z. B. RESTEasy) arbeiten möchten, versuchen Sie Folgendes:

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

mit javax.ws.rs.core.Responseundcom.google.common.io.ByteStreams

Elektrobabe
quelle
-1

Ich habe es noch einfacher gemacht. Problem: Eine CSS-Datei hatte URL-Links zum Ordner img. Erhält 404.

Ich habe mir die URL http: // tomcatfolder: port / img / blablah.png angesehen , die nicht existiert. Aber das deutet wirklich auf die ROOT-App in Tomcat hin.

Also habe ich einfach den img-Ordner von meiner Webanwendung in diese ROOT-App kopiert. Funktioniert!

Natürlich nicht für die Produktion empfohlen, aber dies ist für eine interne Tool-Entwickler-App.

Josef.B
quelle