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ąć.

Janne
źródło

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ż:

BalusC
źródło
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ść.

Bozho
źródło
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>
niebieskie niebo
źródło
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
sbabamca
źródło
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)

Do Nhu Vy
źródło
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.
JPG
źródło
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>
Yash
źródło
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

electrobabe
źródło
-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.

Josef.B
źródło