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

131

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

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

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

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

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

Janne
источник

Ответы:

161

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

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

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

BalusC
источник
1
@SalutonMondo: путь с наименьшими усилиями.
BalusC
@BalusC, я пробовал это: <Context docBase="/path/to/images" path="/images" />в Windows, но получаю путь относительно папки webapps:C:\install\apache-tomcat-8.0.26\webapps\tmp] is not valid
ACV
В Windows это должно быть:<Context docBase="C:\tmp\" path="/images" />
ACV,
И получение HTTP Status 404 - /images/при выполнении:, http://localhost:8080/images/НО конкретный файл из-под tmpработает: http://localhost:8080/images/Tulips.jpgОК
ACV
Таким образом, если я помещу изображение в папку, ссылка на изображение даст ответ 404, если вы не перезапустите сервер. Есть ли для этого какие-либо причины?
Mateus Viccari 01
9

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

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

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

Bozho
источник
2
Для этого вам действительно не нужен Java 2D API, это только добавит дополнительных накладных расходов. Просто прочтите InputStream и напишите в OutputStream.
BalusC
1
Да, я начал ответ с идеи масштабирования и других манипуляций, но в итоге упростил его.
Bozho
7

Добавьте в 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>
голубое небо
источник
С изменением в web.xml я могу получить список файлов, чтобы отправить его в поле выбора?
Tomasz Waszczyk
1
это изменение будет в web.xml tomcat, а не в вашем приложении
vsingh
Спасибо за это! Требуется ли включение параметра списка файлов dir в web.xml?
dariru 07
6

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

Шаг 1:
Создайте папку в веб-приложениях сервера 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:
для доступа к изображениям на локальном жестком диске (вне корневого каталога)

http://localhost:8080/myproj/images/myfoto.jpg
sbabamca
источник
не могли бы вы подсказать мне, как сделать то же самое для динамических значений. Я имею в виду, что хочу записать данные (xml) в свой локальный каталог или и прочитать их на моей странице jsp. Есть ли способ записать на сервер, управляемый в каталог, чтобы я мог получить к ним доступ, используя вышеуказанную процедуру?
Sudip7
хотя я могу запустить файл index.html правильно, но изображения не отображаются в веб-браузере
rogerwar
Мой пост об ошибке работает нормально Я просто забыл поставить / в конце E: / myproj Я изменил это на E: / myproj /, и он работает нормально Спасибо @sbabamca
rogerwar
Привет, спасибо за сообщение, и это очень полезно. Здесь я хочу загрузить файлы через интерфейс в этот конкретный каталог. Я хочу включить метод POST для того же. Может ли кто-нибудь помочь мне в том же.
vicky
6

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

До Нху Вы
источник
0

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

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

Прочтите 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

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

electrobabe
источник
-1

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

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

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

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

Josef.B
источник