Самый простой способ обслуживания статических данных извне сервера приложений в веб-приложении Java

У меня есть веб-приложение Java работает на Tomcat. Я хочу загрузить статические изображения, которые будут отображаться как в веб-интерфейсе, так и в PDF-файлах, созданных приложением. Новые изображения будут добавлены и сохранены загрузки через веб-интерфейс.

Это не проблема, чтобы сделать это, имея статические данные, хранящиеся в веб-контейнере, но хранение и загрузка их из-за пределов веб-контейнера дает мне головную боль.

Я бы предпочел не использовать отдельный веб-сервер как Apache для обслуживания статических данных на данный момент. Мне также не нравится идея хранения изображений в двоичном формате в базе данных.

Я видел некоторые предложения, такие как каталог изображений, являющийся символической ссылкой, указывающей на каталог за пределами веб-контейнера, но будет ли этот подход работать как в Windows, так и в средах *nix?

некоторые предлагают написать фильтр или сервлет для обработки изображения, но эти предложения были очень расплывчатыми и высокого уровня без указателей на более подробную информацию о том, как это сделать.

10 ответов


я видел некоторые предложения, такие как каталог изображений, являющийся символической ссылкой, указывающей на каталог за пределами веб-контейнера, но будет ли этот подход работать как в Windows, так и в средах *nix?

если вы придерживаетесь правил пути к файловой системе *nix (т. е. вы используете исключительно косые черты, как в /path/to/files), то он будет работать на Windows, а также без необходимости возиться с уродливым File.separator string-конкатенации. Это однако сканироваться следует только на том же рабочем диске, откуда была вызвана эта команда. Поэтому, если Tomcat, например, установлен на C: тут /path/to/files фактически указывает на C:\path\to\files.

если все файлы расположены за пределами webapp, и вы хотите иметь Tomcat's DefaultServlet чтобы справиться с ними, то все, что вам в основном нужно сделать в Tomcat, это добавить следующий элемент контекста в /conf/server.xml внутри <Host> tag:

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

таким образом они будут доступны через http://example.com/files/.... Пример конфигурации GlassFish/Payara можно найти здесь и пример конфигурации WildFly можно найти здесь.

если вы хотите иметь контроль над чтением/записью файлов самостоятельно, то вам нужно создать Servlet для этого, который в основном просто получает InputStream файла во вкусе например FileInputStream и записывает его в OutputStream на HttpServletResponse.

в ответе вы должны установить Content-Type заголовок, чтобы клиент знал, какое приложение связать с предоставленным файлом. И, вы должны установить Content-Length заголовок, чтобы клиент мог рассчитать ход загрузки, иначе он будет неизвестен. И, вы должны установить Content-Disposition заголовок attachment если вы хотите Сохранить Как диалог, в противном случае клиент попытается отобразить его в строке. Наконец, просто напишите содержимое файла в выходной поток ответа.

вот основной пример такого сервлет:

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

}

при отображении на url-pattern, например /files/*, тогда вы можете назвать его http://example.com/files/image.png. Таким образом, вы можете иметь больше контроля над запросами, чем DefaultServlet, например, обеспечивая изображения по умолчанию (т. е. if (!file.exists()) file = new File("/path/to/files", "404.gif") или так). Также с помощью request.getPathInfo() предпочтительно выше request.getParameter() потому что это более SEO дружественный и в противном случае IE не будет выбирать правильное имя файла во время Сохранить Как.

вы можете использовать ту же логику для обслуживания файлов из базы данных. Просто замените new FileInputStream() by ResultSet#getInputStream().

надеюсь, что это помогает.

Читайте также:


вы можете сделать это, поставив свои изображения на фиксированный путь (например: /var / images, или c:\images), добавьте параметр в настройках приложения(представлен в моем примере настройками.class), и загрузите их так, в 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);
}

или если вы хотите управлять изображением:

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

тогда HTML код будет <img src="imageServlet?imageName=myimage.png" />

конечно, вы должны думать об обслуживании различных типов контента - "image / jpeg", например, на основе расширение файла. Также вы должны предоставить кэширование.

кроме того, вы можете использовать этот сервлет для качественного масштабирования ваших изображений, предоставляя параметры ширины и высоты в качестве аргументов и используя image.getScaledInstance(w, h, Image.SCALE_SMOOTH), учитывая производительность, конечно.


требование: доступ к статическим ресурсам (изображения / видео., п.,) извне каталога WEBROOT или с локального диска

Шаг 1 :
Создайте папку под webapps сервера tomcat., скажем, имя папки-myproj

Шаг 2 :
под myproj создайте папку WEB-INF под этим создайте простой веб.в XML

код в разделе web.в XML

<web-app>
</web-app>

структура каталогов для вышеуказанных двух шаги

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

Шаг 3:
Теперь создайте xml-файл с именем myproj.в XML находится в следующем месте:

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

код в myproj.XML-код:

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

Шаг 4:
4 A) Теперь создайте папку с именем myproj на диске вашего жесткого диска и создайте новый

папка с названием изображения и некоторые изображения в папке images (e:myproj\images\)

предположим myfoto.jpg является помещенных под e:\myproj\images\myfoto.jpg

4 B) Теперь создайте папку с именем WEB-INF в e:\myproj\WEB-INF и создать сеть.xml в папке WEB-INF

код в web.в XML

<web-app>
</web-app>

Шаг 5:
теперь создать .html-документ с индексом имени.html и место под e:\myproj

код под индексом.формат html Добро пожаловать иметь вид myproj

структура каталогов для вышеуказанного шага 4 и шага 5 выглядит следующим образом

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

Шаг 6:
теперь запустите сервер Apache tomcat

Шаг 7:
откройте браузер и введите url следующим образом

http://localhost:8080/myproj    

затем u отображает содержимое, которое предоставляется в индексе.HTML-код

Шаг 8:
для доступа к изображениям под локальным жестким диском (за пределами webroot)

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

добавить на сервер.XML-код :

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

включить параметр списка файлов dir в интернете.XML-код :

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

это история с моего рабочего места:
- Мы пытаемся загрузить умножить изображения и файлы документов, используя стойки 1 и Tomcat 7.x.
- Мы пытаемся записать загруженные файлы в файловую систему, имя файла и полный путь к записям базы данных.
- Мы стараемся отдельным файлом папки за пределами веб-приложения каталог. (*)

приведенное ниже решение довольно простое, эффективное для требования (*):

в файле META-INF/context.xml С следующего содержания: (Например, мое приложение выполняется в http://localhost:8080/ABC Мои приложения / проекта ABC). (это также полное содержимое файла context.xml)

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

(работает с Tomcat версии 7 или более поздней версии)

результат: мы были созданы 2 псевдоним. Например, мы сохраняем изображения по адресу:D:\images\foo.jpg и просмотр по ссылке или с помощью тега изображения:

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

или

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

(Я использую Netbeans 7.x, Netbeans, кажется, автоматически создает файл WEB-INF\context.xml)


если вы решите направить в FileServlet тогда вам также понадобится allowLinking="true" на context.xml для того чтобы FileServlet для просмотра ссылки.

см.http://tomcat.apache.org/tomcat-6.0-doc/config/context.html


если кто-то не в состоянии решить свою проблему с принятым ответом, то обратите внимание на следующие соображения:

  1. нет необходимости упоминать localhost:<port> с .

прочитайте InputStream файла и напишите его в ServletOutputStream для отправки двоичных данных клиенту.

  • локальный файл вы можете прочитать файл напрямую, используя FileInputStream ('путь / изображение.png').
  • файл базы данных Mongo вы можете получить InputStream с помощью GridFS.
@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));
    }
}

результат URL непосредственно к src атрибута.

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

Если вы хотите работать с JAX-RS (например, RESTEasy) попробуйте следующее:

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

используя javax.ws.rs.core.Response и com.google.common.io.ByteStreams


Я сделал это еще проще. Проблема: файл CSS имел url-ссылки на папку img. Получает 404.

Я посмотрел на url,http://tomcatfolder:port/img/blablah.формат PNG, которого не существует. Но это действительно указывает на корневое приложение в Tomcat.

поэтому я просто скопировал папку img из моего webapp в это корневое приложение. Работает!

Не рекомендуется для производства, конечно, но это для внутреннего инструмента dev приложения.