Servlet zum Bereitstellen von statischem Inhalt


145

Ich stelle eine Webanwendung auf zwei verschiedenen Containern bereit (Tomcat und Jetty), aber ihre Standardservlets zum Bereitstellen des statischen Inhalts haben eine andere Art, mit der URL-Struktur umzugehen, die ich verwenden möchte ( Details ).

Ich möchte daher ein kleines Servlet in die Webanwendung aufnehmen, um seinen eigenen statischen Inhalt (Bilder, CSS usw.) bereitzustellen. Das Servlet sollte die folgenden Eigenschaften haben:

  • Keine externen Abhängigkeiten
  • Einfach und zuverlässig
  • Unterstützung für If-Modified-SinceHeader (dh benutzerdefinierte getLastModifiedMethode)
  • (Optional) Unterstützung für GZIP-Codierung, Etags, ...

Ist ein solches Servlet irgendwo verfügbar? Das nächste, das ich finden kann, ist Beispiel 4-10 aus dem Servlet-Buch.

Update: Die URL-Struktur, die ich verwenden möchte - falls Sie sich fragen - ist einfach:

    <servlet-mapping>
            <servlet-name>main</servlet-name>
            <url-pattern>/*</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
            <servlet-name>default</servlet-name>
            <url-pattern>/static/*</url-pattern>
    </servlet-mapping>

Daher sollten alle Anforderungen an das Hauptservlet übergeben werden, es sei denn, sie beziehen sich auf den staticPfad. Das Problem ist, dass das Standardservlet von Tomcat den ServletPath nicht berücksichtigt (also sucht es nach den statischen Dateien im Hauptordner), während Jetty dies tut (also sucht es im staticOrdner).


Könnten Sie die "URL-Struktur" erläutern, die Sie verwenden möchten? Das Rollen Ihrer eigenen, basierend auf dem verknüpften Beispiel 4-10, scheint eine triviale Anstrengung zu sein. Ich habe es selbst oft gemacht ...
Stu Thompson

Ich habe meine Frage bearbeitet, um die URL-Struktur auszuarbeiten. Und ja, am Ende habe ich mein eigenes Servlet gerollt. Siehe meine Antwort unten.
Bruno De Fraine

1
Warum verwenden Sie den Webserver nicht für statische Inhalte?
Stephen

4
@ Stephen: weil es nicht immer einen Apachen vor dem Tomcat / Jetty gibt. Und um den Aufwand einer separaten Konfiguration zu vermeiden. Aber Sie haben Recht, ich könnte diese Option in Betracht ziehen.
Bruno De Fraine

Ich kann einfach nicht verstehen, warum Sie kein Mapping wie dieses verwendet haben. <Servlet-Mapping> <Servlet-Name> Standard </ Servlet-Name> <URL-Muster> / </ URL-Muster> </ Servlet-Mapping > um statischen Inhalt zu
liefern

Antworten:


53

Ich habe eine etwas andere Lösung gefunden. Es ist ein bisschen hackig, aber hier ist das Mapping:

<servlet-mapping>   
    <servlet-name>default</servlet-name>
    <url-pattern>*.html</url-pattern>
</servlet-mapping>
<servlet-mapping>
    <servlet-name>default</servlet-name>
    <url-pattern>*.jpg</url-pattern>
</servlet-mapping>
<servlet-mapping>
 <servlet-name>default</servlet-name>
    <url-pattern>*.png</url-pattern>
</servlet-mapping>
<servlet-mapping>
    <servlet-name>default</servlet-name>
    <url-pattern>*.css</url-pattern>
</servlet-mapping>
<servlet-mapping>
    <servlet-name>default</servlet-name>
    <url-pattern>*.js</url-pattern>
</servlet-mapping>

<servlet-mapping>
    <servlet-name>myAppServlet</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

Dies ordnet im Grunde nur alle Inhaltsdateien nach Erweiterung dem Standardservlet und alles andere "myAppServlet" zu.

Es funktioniert sowohl in Jetty als auch in Tomcat.


13
Tatsächlich können Sie mehr als ein URL-Muster-Tag in das Servelet-Mapping einfügen;)
Fareed Alnamrouti

5
Servlet 2.5 und
höher

Seien Sie vorsichtig mit Indexdateien (index.html), da diese möglicherweise Vorrang vor Ihrem Servlet haben.
Andres

Ich denke, es ist eine schlechte Idee *.sth. Wenn jemand eine URL example.com/index.jsp?g=.stherhält, erhält er die Quelle der JSP-Datei. Oder irre ich mich? (Ich bin neu in Java EE) Ich benutze normalerweise URL-Muster /css/*und etc.
SemperPeritus

46

In diesem Fall ist keine vollständig benutzerdefinierte Implementierung des Standardservlets erforderlich. Mit diesem einfachen Servlet können Sie die Anforderung in die Implementierung des Containers einschließen:


package com.example;

import java.io.*;

import javax.servlet.*;
import javax.servlet.http.*;

public class DefaultWrapperServlet extends HttpServlet
{   
    public void doGet(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException
    {
        RequestDispatcher rd = getServletContext().getNamedDispatcher("default");

        HttpServletRequest wrapped = new HttpServletRequestWrapper(req) {
            public String getServletPath() { return ""; }
        };

        rd.forward(wrapped, resp);
    }
}

Diese Frage bietet eine übersichtliche Möglichkeit, / einem Controller und / static mithilfe eines Filters statischen Inhalten zuzuordnen. Überprüfen Sie die aktualisierte Antwort nach der akzeptierten: stackoverflow.com/questions/870150/…
David Carboni


30

Ich habe mit FileServlet gute Ergebnisse erzielt , da es fast das gesamte HTTP (Etags, Chunking usw.) unterstützt.


Vielen Dank! Stunden fehlgeschlagener Versuche und schlechter Antworten, und dies löste mein Problem
Yossi Shasho

4
Um Inhalte aus einem Ordner außerhalb der App bereitzustellen (ich verwende sie, um einen Ordner von der Festplatte zu bedienen, z. B. C: \ resources), habe ich diese Zeile geändert: this.basePath = getServletContext (). GetRealPath (getInitParameter ("basePath") ")); Und ersetzt durch: this.basePath = getInitParameter ("basePath");
Yossi Shasho

1
Eine aktualisierte Version ist verfügbar unter showcase.omnifaces.org/servlets/FileServlet
koppor

26

Abstrakte Vorlage für ein statisches Ressourcenservlet

Teilweise basierend auf diesem Blog aus dem Jahr 2007, ist hier eine modernisierte und in hohem Maße wiederverwendbare abstrakte Vorlage für ein Servlet, das sich ordnungsgemäß mit Caching befasst ETag, If-None-Matchund If-Modified-Since(aber keine Unterstützung für Gzip und Range; nur um es einfach zu halten; Gzip könnte mit einem Filter oder via durchgeführt werden Containerkonfiguration).

public abstract class StaticResourceServlet extends HttpServlet {

    private static final long serialVersionUID = 1L;
    private static final long ONE_SECOND_IN_MILLIS = TimeUnit.SECONDS.toMillis(1);
    private static final String ETAG_HEADER = "W/\"%s-%s\"";
    private static final String CONTENT_DISPOSITION_HEADER = "inline;filename=\"%1$s\"; filename*=UTF-8''%1$s";

    public static final long DEFAULT_EXPIRE_TIME_IN_MILLIS = TimeUnit.DAYS.toMillis(30);
    public static final int DEFAULT_STREAM_BUFFER_SIZE = 102400;

    @Override
    protected void doHead(HttpServletRequest request, HttpServletResponse response) throws ServletException ,IOException {
        doRequest(request, response, true);
    }

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doRequest(request, response, false);
    }

    private void doRequest(HttpServletRequest request, HttpServletResponse response, boolean head) throws IOException {
        response.reset();
        StaticResource resource;

        try {
            resource = getStaticResource(request);
        }
        catch (IllegalArgumentException e) {
            response.sendError(HttpServletResponse.SC_BAD_REQUEST);
            return;
        }

        if (resource == null) {
            response.sendError(HttpServletResponse.SC_NOT_FOUND);
            return;
        }

        String fileName = URLEncoder.encode(resource.getFileName(), StandardCharsets.UTF_8.name());
        boolean notModified = setCacheHeaders(request, response, fileName, resource.getLastModified());

        if (notModified) {
            response.sendError(HttpServletResponse.SC_NOT_MODIFIED);
            return;
        }

        setContentHeaders(response, fileName, resource.getContentLength());

        if (head) {
            return;
        }

        writeContent(response, resource);
    }

    /**
     * Returns the static resource associated with the given HTTP servlet request. This returns <code>null</code> when
     * the resource does actually not exist. The servlet will then return a HTTP 404 error.
     * @param request The involved HTTP servlet request.
     * @return The static resource associated with the given HTTP servlet request.
     * @throws IllegalArgumentException When the request is mangled in such way that it's not recognizable as a valid
     * static resource request. The servlet will then return a HTTP 400 error.
     */
    protected abstract StaticResource getStaticResource(HttpServletRequest request) throws IllegalArgumentException;

    private boolean setCacheHeaders(HttpServletRequest request, HttpServletResponse response, String fileName, long lastModified) {
        String eTag = String.format(ETAG_HEADER, fileName, lastModified);
        response.setHeader("ETag", eTag);
        response.setDateHeader("Last-Modified", lastModified);
        response.setDateHeader("Expires", System.currentTimeMillis() + DEFAULT_EXPIRE_TIME_IN_MILLIS);
        return notModified(request, eTag, lastModified);
    }

    private boolean notModified(HttpServletRequest request, String eTag, long lastModified) {
        String ifNoneMatch = request.getHeader("If-None-Match");

        if (ifNoneMatch != null) {
            String[] matches = ifNoneMatch.split("\\s*,\\s*");
            Arrays.sort(matches);
            return (Arrays.binarySearch(matches, eTag) > -1 || Arrays.binarySearch(matches, "*") > -1);
        }
        else {
            long ifModifiedSince = request.getDateHeader("If-Modified-Since");
            return (ifModifiedSince + ONE_SECOND_IN_MILLIS > lastModified); // That second is because the header is in seconds, not millis.
        }
    }

    private void setContentHeaders(HttpServletResponse response, String fileName, long contentLength) {
        response.setHeader("Content-Type", getServletContext().getMimeType(fileName));
        response.setHeader("Content-Disposition", String.format(CONTENT_DISPOSITION_HEADER, fileName));

        if (contentLength != -1) {
            response.setHeader("Content-Length", String.valueOf(contentLength));
        }
    }

    private void writeContent(HttpServletResponse response, StaticResource resource) throws IOException {
        try (
            ReadableByteChannel inputChannel = Channels.newChannel(resource.getInputStream());
            WritableByteChannel outputChannel = Channels.newChannel(response.getOutputStream());
        ) {
            ByteBuffer buffer = ByteBuffer.allocateDirect(DEFAULT_STREAM_BUFFER_SIZE);
            long size = 0;

            while (inputChannel.read(buffer) != -1) {
                buffer.flip();
                size += outputChannel.write(buffer);
                buffer.clear();
            }

            if (resource.getContentLength() == -1 && !response.isCommitted()) {
                response.setHeader("Content-Length", String.valueOf(size));
            }
        }
    }

}

Verwenden Sie es zusammen mit der folgenden Schnittstelle, die eine statische Ressource darstellt.

interface StaticResource {

    /**
     * Returns the file name of the resource. This must be unique across all static resources. If any, the file
     * extension will be used to determine the content type being set. If the container doesn't recognize the
     * extension, then you can always register it as <code>&lt;mime-type&gt;</code> in <code>web.xml</code>.
     * @return The file name of the resource.
     */
    public String getFileName();

    /**
     * Returns the last modified timestamp of the resource in milliseconds.
     * @return The last modified timestamp of the resource in milliseconds.
     */
    public long getLastModified();

    /**
     * Returns the content length of the resource. This returns <code>-1</code> if the content length is unknown.
     * In that case, the container will automatically switch to chunked encoding if the response is already
     * committed after streaming. The file download progress may be unknown.
     * @return The content length of the resource.
     */
    public long getContentLength();

    /**
     * Returns the input stream with the content of the resource. This method will be called only once by the
     * servlet, and only when the resource actually needs to be streamed, so lazy loading is not necessary.
     * @return The input stream with the content of the resource.
     * @throws IOException When something fails at I/O level.
     */
    public InputStream getInputStream() throws IOException;

}

Sie müssen lediglich das angegebene abstrakte Servlet erweitern und die getStaticResource()Methode gemäß Javadoc implementieren .

Konkretes Beispiel für das Dateisystem:

Hier ist ein konkretes Beispiel, das es über eine URL wie /files/foo.extaus dem lokalen Datenträger-Dateisystem bereitstellt:

@WebServlet("/files/*")
public class FileSystemResourceServlet extends StaticResourceServlet {

    private File folder;

    @Override
    public void init() throws ServletException {
        folder = new File("/path/to/the/folder");
    }

    @Override
    protected StaticResource getStaticResource(HttpServletRequest request) throws IllegalArgumentException {
        String pathInfo = request.getPathInfo();

        if (pathInfo == null || pathInfo.isEmpty() || "/".equals(pathInfo)) {
            throw new IllegalArgumentException();
        }

        String name = URLDecoder.decode(pathInfo.substring(1), StandardCharsets.UTF_8.name());
        final File file = new File(folder, Paths.get(name).getFileName().toString());

        return !file.exists() ? null : new StaticResource() {
            @Override
            public long getLastModified() {
                return file.lastModified();
            }
            @Override
            public InputStream getInputStream() throws IOException {
                return new FileInputStream(file);
            }
            @Override
            public String getFileName() {
                return file.getName();
            }
            @Override
            public long getContentLength() {
                return file.length();
            }
        };
    }

}

Konkretes Beispiel aus der Datenbank:

Hier ist ein konkretes Beispiel, das es über eine URL wie /files/foo.extaus der Datenbank über einen EJB-Serviceaufruf bereitstellt, der Ihre Entität mit einer byte[] contentEigenschaft zurückgibt :

@WebServlet("/files/*")
public class YourEntityResourceServlet extends StaticResourceServlet {

    @EJB
    private YourEntityService yourEntityService;

    @Override
    protected StaticResource getStaticResource(HttpServletRequest request) throws IllegalArgumentException {
        String pathInfo = request.getPathInfo();

        if (pathInfo == null || pathInfo.isEmpty() || "/".equals(pathInfo)) {
            throw new IllegalArgumentException();
        }

        String name = URLDecoder.decode(pathInfo.substring(1), StandardCharsets.UTF_8.name());
        final YourEntity yourEntity = yourEntityService.getByName(name);

        return (yourEntity == null) ? null : new StaticResource() {
            @Override
            public long getLastModified() {
                return yourEntity.getLastModified();
            }
            @Override
            public InputStream getInputStream() throws IOException {
                return new ByteArrayInputStream(yourEntityService.getContentById(yourEntity.getId()));
            }
            @Override
            public String getFileName() {
                return yourEntity.getName();
            }
            @Override
            public long getContentLength() {
                return yourEntity.getContentLength();
            }
        };
    }

}

1
Sehr geehrter @BalusC, ich denke, Ihr Ansatz ist anfällig für einen Hacker, der durch Senden der folgenden Anfrage durch das Dateisystem navigieren könnte : files/%2e%2e/mysecretfile.txt. Diese Anfrage erzeugt files/../mysecretfile.txt. Ich habe es auf Tomcat 7.0.55 getestet. Sie nennen es ein Kletterverzeichnis: owasp.org/index.php/Path_Traversal
Cristian Arteaga

1
@Cristian: Ja, möglich. Ich habe das Beispiel aktualisiert, um zu zeigen, wie dies verhindert werden kann.
BalusC

Dies sollte keine positiven Stimmen bekommen. Das Bereitstellen statischer Dateien für eine Webseite mit Servlet wie diesem ist ein Rezept für die Katastrophensicherheit. Alle diese Probleme wurden bereits gelöst, und es gibt keinen Grund, einen neuen benutzerdefinierten Weg mit wahrscheinlich unentdeckteren Sicherheitszeitbomben zu implementieren. Der richtige Pfad besteht darin, Tomcat / GlassFish / Jetty usw. für die Bereitstellung des Inhalts zu konfigurieren oder noch besser einen dedizierten Dateiserver wie NGinX zu verwenden.
Leonhard Printz

@LeonhardPrintz: Ich werde die Antwort löschen und meinen Freunden bei Tomcat Bericht erstatten, sobald Sie auf Sicherheitsprobleme hinweisen. Kein Problem.
BalusC

19

Am Ende rollte ich meine eigenen StaticServlet. Es unterstützt die If-Modified-SinceGZIP-Codierung und sollte auch statische Dateien aus War-Dateien bereitstellen können. Es ist kein sehr schwieriger Code, aber auch nicht ganz trivial.

Der Code ist verfügbar: StaticServlet.java . Fühlen Sie sich frei zu kommentieren.

Update: Khurram fragt nach der ServletUtilsKlasse, auf die verwiesen wird StaticServlet. Es ist einfach eine Klasse mit Hilfsmethoden, die ich für mein Projekt verwendet habe. Die einzige Methode, die Sie benötigen, ist coalesce(die mit der SQL-Funktion identisch ist COALESCE). Dies ist der Code:

public static <T> T coalesce(T...ts) {
    for(T t: ts)
        if(t != null)
            return t;
    return null;
}

2
Nennen Sie nicht Ihren inneren Klassenfehler. Dies kann zu Verwirrung führen, da Sie es mit java.lang.Error verwechseln können. Ist Ihre web.xml auch dieselbe?
Leonel

Danke für die Fehlerwarnung. web.xml ist dieselbe, wobei "default" durch den Namen des StaticServlet ersetzt wird.
Bruno De Fraine

1
Die Koaleszenzmethode kann (innerhalb der Servlet-Klasse) durch commons-lang StringUtils.defaultString (String, String) ersetzt werden
Mike Minicki

Die transferStreams () -Methode kann auch durch Files.copy (is, os) ersetzt werden.
Gerrit Brink

Warum ist dieser Ansatz so beliebt? Warum implementieren Leute solche statischen Dateiserver neu? Es gibt so viele Sicherheitslücken, die nur darauf warten, entdeckt zu werden, und so viele Funktionen von echten statischen Dateiservern, die nicht implementiert sind.
Leonhard Printz

12

Nach den obigen Beispielinformationen zu urteilen, basiert dieser gesamte Artikel auf einem fehlerhaften Verhalten in Tomcat 6.0.29 und früheren Versionen. Siehe https://issues.apache.org/bugzilla/show_bug.cgi?id=50026 . Aktualisieren Sie auf Tomcat 6.0.30 und das Verhalten zwischen (Tomcat | Jetty) sollte zusammengeführt werden.


1
Das ist auch mein Verständnis von svn diff -c1056763 http://svn.apache.org/repos/asf/tomcat/tc6.0.x/trunk/. Endlich, nachdem ich dieses WONTFIX vor +3 Jahren markiert habe!
Bruno De Fraine

12

Versuche dies

<servlet-mapping>
    <servlet-name>default</servlet-name>
    <url-pattern>*.js</url-pattern>
    <url-pattern>*.css</url-pattern>
    <url-pattern>*.ico</url-pattern>
    <url-pattern>*.png</url-pattern>
    <url-pattern>*.jpg</url-pattern>
    <url-pattern>*.htc</url-pattern>
    <url-pattern>*.gif</url-pattern>
</servlet-mapping>    

Bearbeiten: Dies gilt nur für die Servlet 2.5-Spezifikation und höher.


Dies scheint keine gültige Konfiguration zu sein.
Gedrox

10

Ich hatte das gleiche Problem und löste es mit dem Code des 'Standard-Servlets' aus der Tomcat-Codebasis.

https://github.com/apache/tomcat/blob/master/java/org/apache/catalina/servlets/DefaultServlet.java

Das DefaultServlet ist das Servlet, das die statischen Ressourcen (JPG, HTML, CSS, GIF usw.) in Tomcat bereitstellt.

Dieses Servlet ist sehr effizient und hat einige der oben definierten Eigenschaften.

Ich denke, dass dieser Quellcode ein guter Weg ist, um die Funktionen oder Abhängigkeiten zu starten und zu entfernen, die Sie nicht benötigen.

  • Verweise auf das Paket org.apache.naming.resources können entfernt oder durch den Code java.io.File ersetzt werden.
  • Verweise auf das Paket org.apache.catalina.util sind wahrscheinlich nur Dienstprogrammmethoden / -klassen, die in Ihrem Quellcode dupliziert werden können.
  • Verweise auf die Klasse org.apache.catalina.Globals können eingefügt oder entfernt werden.

Es scheint von vielen Sachen abzuhängen org.apache.*. Wie können Sie es mit Jetty verwenden?
Bruno De Fraine

Sie haben Recht, diese Version hat zu viele Abhängigkeiten zum Tomcat (und sie unterstützt auch viele Dinge, die Sie vielleicht nicht wollen. Ich werde meine Antwort bearbeiten.
Panagiotis Korros


4

Dazu habe ich das tomcat DefaultServlet ( src ) erweitert und die Methode getRelativePath ( ) überschrieben.

package com.example;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import org.apache.catalina.servlets.DefaultServlet;

public class StaticServlet extends DefaultServlet
{
   protected String pathPrefix = "/static";

   public void init(ServletConfig config) throws ServletException
   {
      super.init(config);

      if (config.getInitParameter("pathPrefix") != null)
      {
         pathPrefix = config.getInitParameter("pathPrefix");
      }
   }

   protected String getRelativePath(HttpServletRequest req)
   {
      return pathPrefix + super.getRelativePath(req);
   }
}

... und hier sind meine Servlet-Zuordnungen

<servlet>
    <servlet-name>StaticServlet</servlet-name>
    <servlet-class>com.example.StaticServlet</servlet-class>
    <init-param>
        <param-name>pathPrefix</param-name>
        <param-value>/static</param-value>
    </init-param>       
</servlet>

<servlet-mapping>
    <servlet-name>StaticServlet</servlet-name>
    <url-pattern>/static/*</url-pattern>
</servlet-mapping>  

1

Um alle Anforderungen einer Spring-App sowie von /favicon.ico und den JSP-Dateien von / WEB-INF / jsp / * zu bearbeiten, die Spring in AbstractUrlBasedView anfordert, können Sie einfach das jsp-Servlet und das Standardservlet neu zuordnen:

  <servlet>
    <servlet-name>springapp</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>

  <servlet-mapping>
    <servlet-name>jsp</servlet-name>
    <url-pattern>/WEB-INF/jsp/*</url-pattern>
  </servlet-mapping>

  <servlet-mapping>
    <servlet-name>default</servlet-name>
    <url-pattern>/favicon.ico</url-pattern>
  </servlet-mapping>

  <servlet-mapping>
    <servlet-name>springapp</servlet-name>
    <url-pattern>/*</url-pattern>
  </servlet-mapping>

Wir können uns bei der Standardzuordnung für das jsp-Servlet nicht auf das URL-Muster * .jsp verlassen, da das Pfadmuster '/ *' übereinstimmt, bevor eine Erweiterungszuordnung überprüft wird. Wenn Sie das JSP-Servlet einem tieferen Ordner zuordnen, wird es zuerst abgeglichen. Der Abgleich von '/favicon.ico' erfolgt genau vor dem Abgleich des Pfadmusters. Tiefere Pfadübereinstimmungen funktionieren oder genaue Übereinstimmungen, aber keine Erweiterungsübereinstimmungen können über die Pfadübereinstimmung '/ *' hinausgehen. Die Zuordnung von '/' zum Standardservlet scheint nicht zu funktionieren. Sie würden denken, das genaue '/' würde das '/ *' - Pfadmuster auf springapp übertreffen.

Die obige Filterlösung funktioniert nicht für weitergeleitete / eingeschlossene JSP-Anforderungen aus der Anwendung. Damit es funktioniert, musste ich den Filter direkt auf springapp anwenden. Zu diesem Zeitpunkt war der URL-Mustervergleich nutzlos, da alle Anforderungen, die an die Anwendung gehen, auch an deren Filter gehen. Also habe ich dem Filter einen Mustervergleich hinzugefügt und dann etwas über das 'jsp'-Servlet erfahren und festgestellt, dass das Pfadpräfix nicht wie beim Standard-Servlet entfernt wird. Das löste mein Problem, das nicht genau das gleiche war, aber häufig genug.


1

Auf Tomcat 8.x geprüft: Statische Ressourcen funktionieren einwandfrei, wenn das Root-Servlet "" zugeordnet ist. Für Servlet 3.x könnte es von gemacht werden@WebServlet("")


0

Verwenden Sie org.mortbay.jetty.handler.ContextHandler. Sie benötigen keine zusätzlichen Komponenten wie StaticServlet.

Am Steghaus,

$ cd Kontexte

$ cp javadoc.xml static.xml

$ vi static.xml

...

<Configure class="org.mortbay.jetty.handler.ContextHandler">
<Set name="contextPath">/static</Set>
<Set name="resourceBase"><SystemProperty name="jetty.home" default="."/>/static/</Set>
<Set name="handler">
  <New class="org.mortbay.jetty.handler.ResourceHandler">
    <Set name="cacheControl">max-age=3600,public</Set>
  </New>
 </Set>
</Configure>

Legen Sie den Wert von contextPath mit Ihrem URL-Präfix fest und legen Sie den Wert von resourceBase als Dateipfad des statischen Inhalts fest.

Es hat bei mir funktioniert.


Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.