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

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

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

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

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

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

Ответы [ 10 ]

153 голосов
/ 28 ноября 2009

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

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

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

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

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

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

Onответ, вы должны установить заголовок 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() на ResultSet#getInputStream().

Надеюсь, это поможет.

См. Также:

9 голосов
/ 28 ноября 2009

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

6 голосов
/ 16 марта 2013

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

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

Шаг 2:
В myproj создайте папку WEB-INF, в которой создайте простой файл web.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 на E-диске вашего жесткого диска и создайте новый

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

Предположим, myfoto.jpg находится под e:\myproj\images\myfoto.jpg

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

Код в web.xml

<web-app>
</web-app>

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

КОД в index.html Добро пожаловать в Myproj

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

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

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

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

http://localhost:8080/myproj    

тогда вы отображаете содержимое, которое предоставляется в index.html

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

http://localhost:8080/myproj/images/myfoto.jpg
5 голосов
/ 02 июня 2015

Это история с моего рабочего места:
- Мы пытаемся загружать несколько изображений и файлы документов, используя Struts 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)

5 голосов
/ 12 мая 2015

Добавить в server.xml:

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

Включить параметр листинга файла dir в web.xml:

    <init-param>
        <param-name>listings</param-name>
        <param-value>true</param-value>
    </init-param>
2 голосов
/ 25 января 2011

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

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

0 голосов
/ 03 апреля 2018

Если вы хотите работать с 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

0 голосов
/ 12 января 2018

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

@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>
0 голосов
/ 26 октября 2017

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

  1. не нужно упоминать localhost:<port> с атрибутом <img> src.
  2. убедитесь, что вы запускаете этот проект вне eclipse, поскольку eclipse самостоятельно создает запись context docBase в своем локальном файле server.xml.
0 голосов
/ 31 августа 2016

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

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

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

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

...