Najprostszy sposób obsługi danych statycznych spoza serwera aplikacji w aplikacji internetowej Java


131

Mam aplikację internetową Java uruchomioną na serwerze Tomcat. Chcę załadować statyczne obrazy, które będą wyświetlane zarówno w interfejsie WWW, jak iw plikach PDF generowanych przez aplikację. Nowe obrazy zostaną również dodane i zapisane poprzez przesłanie ich za pośrednictwem interfejsu internetowego.

Nie stanowi to problemu, ponieważ dane statyczne są przechowywane w kontenerze WWW, ale przechowywanie i ładowanie ich spoza kontenera internetowego przyprawia mnie o ból głowy.

Wolałbym nie używać oddzielnego serwera internetowego, takiego jak Apache, do obsługi danych statycznych w tym momencie. Nie podoba mi się również pomysł przechowywania obrazów w postaci binarnej w bazie danych.

Widziałem kilka sugestii, takich jak posiadanie katalogu obrazu jako symbolicznego łącza wskazującego na katalog poza kontenerem WWW, ale czy to podejście będzie działać zarówno w środowisku Windows, jak i * nix?

Niektórzy sugerują napisanie filtru lub serwletu do obsługi wyświetlanego obrazu, ale te sugestie były bardzo ogólnikowe i nie zawierały wskazówek do bardziej szczegółowych informacji o tym, jak to osiągnąć.

Odpowiedzi:


164

Widziałem kilka sugestii, takich jak posiadanie katalogu obrazu jako symbolicznego łącza wskazującego na katalog poza kontenerem WWW, ale czy to podejście będzie działać zarówno w środowisku Windows, jak i * nix?

Jeśli przestrzegasz zasad ścieżki systemu plików * nix (tj. Używasz wyłącznie ukośników w przód /path/to/files), to będzie działać również w systemie Windows bez potrzeby majstrowania przy brzydkich File.separatorkonkatenacjach ciągów. Byłoby to jednak skanowane tylko na tym samym dysku roboczym, z którego zostało wywołane to polecenie. Więc jeśli na przykład Tomcat jest zainstalowane na C:czym /path/to/filesfaktycznie wskazywać C:\path\to\files.

Jeśli wszystkie pliki znajdują się poza aplikacją internetową i chcesz, aby Tomcat DefaultServletje obsługiwał, to w zasadzie wszystko, co musisz zrobić w Tomcat, to dodać następujący element Context do znacznika /conf/server.xmlwewnętrznego <Host>:

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

W ten sposób będą dostępne przez http://example.com/files/.... W przypadku serwerów opartych na Tomcat, takich jak JBoss EAP 6.x lub starsze, podejście jest zasadniczo takie samo, zobacz także tutaj . Przykład konfiguracji GlassFish / Payara można znaleźć tutaj, a przykład konfiguracji WildFly można znaleźć tutaj .

Jeśli chcesz mieć kontrolę nad odczytu / zapisu plików siebie, to trzeba stworzyć Servletdo tego, który w zasadzie tylko pobiera InputStreamz pliku w smaku na przykład FileInputStreami zapisuje go do OutputStreamz HttpServletResponse.

W odpowiedzi należy ustawić Content-Typenagłówek, aby klient wiedział, którą aplikację skojarzyć z podanym plikiem. I powinieneś ustawić Content-Lengthnagłówek tak, aby klient mógł obliczyć postęp pobierania, w przeciwnym razie będzie nieznany. I powinieneś ustawić Content-Dispositionnagłówek na, attachmentjeśli chcesz okno dialogowe Zapisz jako , w przeciwnym razie klient spróbuje wyświetlić je w tekście. Na koniec po prostu zapisz zawartość pliku w strumieniu wyjściowym odpowiedzi.

Oto podstawowy przykład takiego serwletu:

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

}

Po zmapowaniu na url-patternprzykład /files/*, możesz to wywołać http://example.com/files/image.png. W ten sposób możesz mieć większą kontrolę nad żądaniami niż te DefaultServlet, na przykład dostarczając domyślny obraz (tj. if (!file.exists()) file = new File("/path/to/files", "404.gif")Lub coś podobnego ). Korzystając również request.getPathInfo()jest korzystne przede request.getParameter()ponieważ jest bardziej przyjazny SEO i inaczej IE nie odbierze poprawną nazwę pliku podczas Save As .

Możesz ponownie użyć tej samej logiki do udostępniania plików z bazy danych. Po prostu wymień new FileInputStream()na ResultSet#getInputStream().

Mam nadzieję że to pomoże.

Zobacz też:


1
@SalutonMondo: droga przy najmniejszym wysiłku.
— BalusC,

@BalusC, próbowałem tego: <Context docBase="/path/to/images" path="/images" />w systemie Windows, ale otrzymuję ścieżkę względem folderu webapps:C:\install\apache-tomcat-8.0.26\webapps\tmp] is not valid
— ACV

W systemie Windows powinno to być:<Context docBase="C:\tmp\" path="/images" />
— ACV

A dostaniesz HTTP Status 404 - /images/podczas robienia http://localhost:8080/images/:, ALE konkretny plik z tmppracy: http://localhost:8080/images/Tulips.jpgjest OK
— ACV

W ten sposób, jeśli umieszczę obraz w folderze, link do obrazu da odpowiedź 404, chyba że zrestartujesz serwer, czy jest jakiś powód?
— Mateus Viccari

9

Możesz to zrobić, umieszczając swoje obrazy na stałej ścieżce (na przykład: / var / images lub c: \ images), dodaj ustawienie w ustawieniach aplikacji (reprezentowane w moim przykładzie przez Settings.class) i załaduj je w ten sposób w swoim HttpServlet:

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

Lub jeśli chcesz manipulować obrazem:

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

wtedy kod html byłby <img src="imageServlet?imageName=myimage.png" />

Oczywiście powinieneś pomyśleć o udostępnianiu różnych typów treści - „obraz / jpeg”, na przykład na podstawie rozszerzenia pliku. Powinieneś także zapewnić trochę buforowania.

Ponadto możesz użyć tego serwletu do przeskalowania jakości obrazów, podając parametry szerokości i wysokości jako argumenty i używając image.getScaledInstance(w, h, Image.SCALE_SMOOTH), oczywiście biorąc pod uwagę wydajność.


2
Naprawdę nie potrzebujesz do tego Java 2D API, tylko niepotrzebnie zwiększyłoby to obciążenie. Po prostu przeczytaj InputStream i napisz do OutputStream.
— BalusC

1
Tak, zacząłem odpowiedź od pomysłu przeskalowania i innych manipulacji, ale ostatecznie uprościłem to.
— Bozho

8

Dodaj do server.xml:

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

Włącz parametr listy plików dir w web.xml:

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

Dzięki zmianie w web.xml mogę uzyskać listę plików w celu wysłania go do zaznaczonego pola?
— Tomasz Waszczyk

1
ta zmiana będzie w pliku web.xml tomcat, a nie w aplikacji
— vsingh

1
Dzięki za to! Czy konieczne jest włączenie parametru listy plików dir w web.xml?
— dariru

7

Warunek: dostęp do statycznych zasobów (obrazów / filmów itp.) Spoza katalogu WEBROOT lub z dysku lokalnego

Krok 1:
Utwórz folder pod aplikacjami internetowymi serwera Tomcat. Powiedzmy, że nazwa folderu to myproj

Krok 2:
W myproj utwórz folder WEB-INF w tym celu utwórz prosty plik web.xml

kod pod web.xml

<web-app>
</web-app>

Struktura katalogów dla powyższych dwóch kroków

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

Krok 3:
Teraz utwórz plik xml o nazwie myproj.xml w następującej lokalizacji

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

KOD w myproj.xml:

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

Krok 4:
4 A) Teraz utwórz folder o nazwie myproj na dysku E swojego dysku twardego i utwórz nowy

folder z nazwami obrazów i umieść niektóre obrazy w folderze obrazów (e:myproj\images\)

Załóżmy, że myfoto.jpg znajduje się pod e:\myproj\images\myfoto.jpg

4 B) Teraz utwórz folder o nazwie WEB-INF w e:\myproj\WEB-INFi utwórz plik web.xml w folderze WEB-INF

Kod w web.xml

<web-app>
</web-app>

Krok 5:
Teraz utwórz dokument .html o nazwie index.html i umieść go pod e: \ myproj

KOD pod index.html Witamy w Myproj

Struktura katalogów dla powyższych kroków 4 i 5 jest następująca

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

Krok 6:
Teraz uruchom serwer apache tomcat

Krok 7:
Otwórz przeglądarkę i wpisz adres URL w następujący sposób

http://localhost:8080/myproj    

następnie u wyświetl zawartość zawartą w pliku index.html

Krok 8:
aby uzyskać dostęp do obrazów z lokalnego dysku twardego (poza webrootem)

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

czy możesz mi zasugerować, jak zrobić to samo dla wartości dynamicznych. Mam na myśli, że chcę zapisać dane (xml) do mojego lokalnego katalogu lub i przeczytać to na mojej stronie jsp. Czy istnieje sposób na zapisanie na serwerze zarządzanym do katalogu, abym uzyskał do nich dostęp za pomocą powyższej procedury?
— Sudip7,

chociaż mogę poprawnie uruchomić plik index.html, ale obrazy nie wyświetlają się w przeglądarce internetowej
— rogerwar

Mój błędny post działa dobrze Po prostu zapomniałem wstawić / na końcu E: / myproj Zmieniam to na E: / myproj / i działa dobrze Dzięki @sbabamca
— rogerwar

Cześć, dzięki za post i jest bardzo przydatny. Tutaj chciałbym przesłać pliki przez interfejs do tego konkretnego katalogu. Chcę włączyć metodę POST dla tego samego. Czy ktoś może mi pomóc w tym samym.
— vicky

6

Oto historia z mojego miejsca pracy:
- Staramy się przesyłać powielone obrazy i pliki dokumentów za pomocą Struts 1 i Tomcat 7.x.
- Staramy się zapisywać przesłane pliki do systemu plików, nazwę pliku i pełną ścieżkę do rekordów bazy danych.
- Próbujemy oddzielić foldery plików poza katalogiem aplikacji internetowych . (*)

Poniższe rozwiązanie jest dość proste, skuteczne w przypadku wymagania (*):

W pliku META-INF/context.xmlo następującej treści: (Przykład, moja aplikacja działa w http://localhost:8080/ABC, moja aplikacja / projekt o nazwie ABC). (jest to również pełna zawartość pliku context.xml)

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

(działa z Tomcat w wersji 7 lub nowszej)

Wynik: utworzyliśmy 2 alias. Na przykład zapisujemy obrazy pod adresem: D:\images\foo.jpg i wyświetlamy z linku lub za pomocą tagu obrazu:

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

lub

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

(Używam Netbeans 7.x, Netbeans wydaje się automatycznie tworzyć plik WEB-INF\context.xml)



0

jeśli ktoś nie jest w stanie rozwiązać swojego problemu za pomocą zaakceptowanej odpowiedzi, zwróć uwagę na poniższe uwagi:

  1. nie trzeba wspominać localhost:<port>z <img> srcatrybutem.
  2. upewnij się, że uruchamiasz ten projekt poza zaćmieniem, ponieważ eclipse context docBasesamodzielnie tworzy wpis w swoim lokalnym server.xmlpliku.

0

Przeczytaj InputStream pliku i zapisz go w ServletOutputStreamcelu wysłania danych binarnych do klienta.

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

Podaj adres URL bezpośrednio do srcatrybutu.

<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

Jeśli chcesz pracować z JAX-RS (np. RESTEasy), spróbuj tego:

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

za pomocą javax.ws.rs.core.Responseicom.google.common.io.ByteStreams


-1

Zrobiłem to jeszcze prościej. Problem: plik CSS zawiera linki URL do folderu img. Pobiera 404.

Spojrzałem na adres URL, http: // tomcatfolder: port / img / blablah.png , który nie istnieje. Ale to naprawdę wskazuje na aplikację ROOT w Tomcat.

Więc właśnie skopiowałem folder img z mojej aplikacji internetowej do tej aplikacji ROOT. Pracuje!

Oczywiście nie jest to zalecane do produkcji, ale dotyczy to wewnętrznej aplikacji do tworzenia narzędzi.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.