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

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

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

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

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

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

11 ответов

Решение

Я видел некоторые предложения, например, наличие в каталоге изображений символической ссылки, указывающей на каталог вне веб-контейнера, но будет ли этот подход работать как в средах 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,

На ответ, вы должны установить 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(),

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

Смотрите также:

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

Требование: доступ к статическим ресурсам (изображения / видео и т. Д.) Из-за пределов каталога 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 вашего жесткого диска и создайте новый

папка с именем images и помещением некоторых изображений в папку images (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

Добавить в 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>

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

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

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

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

Прочитайте 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://stackru.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>

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

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

Вы можете редактировать 2 файла вconfкаталог:
Сначала отредактируйтеserver.xmlфайл:
Вы увидите<Engine></Engine>имеет много<Host></Host>ярлык.
в<Host>с именем: «localhost», изменить<Context>к:

      <Host>
    <Valve className="org.apache.catalina.authenticator.SingleSignOn" />
    ...
    <Context docBase="/home/your_directory_contain_image/" reloadable="true" path="images"></Context>
</Host>

с:
docBaseэто путь к каталогу, содержащему ваши изображения.
path: путь, который будет добавлен к URL-адресу при показе изображения.
Вашей цели понравится: http://localhost:8080/images/image1.png

Далее редактируемweb.xmlфайл, в<servlet>с именем:<servlet-name>default</servlet-name>редактировать как:

          <servlet>
        <servlet-name>default</servlet-name>
        <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
        <init-param>
            <param-name>debug</param-name>
            <param-value>0</param-value>
        </init-param>
        <init-param>
            <param-name>listings</param-name>
            <param-value>true</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

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

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

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

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

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

Другие вопросы по тегам