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


131

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

Це не проблема, якщо статичні дані зберігаються у веб-контейнері, але зберігання та завантаження їх поза веб-контейнером доставляє мені головний біль.

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

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

Деякі пропонують написати фільтр або сервлет для обробки подачі зображень, але ці пропозиції були дуже розпливчастими та високорівневими без покажчиків на більш детальну інформацію про те, як це досягти.

Відповіді:


161

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

Якщо ви дотримуєтесь правил шляху до файлової системи * nix (тобто ви використовуєте виключно передні косої риски, як у /path/to/files), то вона також буде працювати і в Windows, без необхідності поспішати з потворними File.separatorрядками. Однак він буде сканований лише на тому ж робочому диску, що і звідки викликана ця команда. Отже, якщо Tomcat, наприклад, встановлений на, C:тоді це /path/to/filesнасправді вказувати б C:\path\to\files.

Якщо всі файли знаходяться за межами веб-сторінки, і ви хочете, щоб Tomcat працював з DefaultServletними, то все, що вам потрібно зробити в Tomcat, - це додати наступний елемент контексту до /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 не вибере правильне ім’я файлу під час Save As .

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

Сподіваюся, це допомагає.

Дивитися також:


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

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

Звичайно, вам слід подумати про розміщення різних типів вмісту - "image / jpeg", наприклад на основі розширення файлу. Також слід надати кешування.

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


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

1
Так, я почав відповідь ідеєю зміни масштабу та інших маніпуляцій, але закінчив спростити її.
Божо

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 я можу отримати список файлів для того, щоб надіслати його до вибраного поля?
Томаш Ващик

1
ця зміна буде в tomcat's web.xml, а не у вашій заяві
vsingh

Дякую за це! Чи потрібно включити параметр переліку файлів dir у web.xml?
dariru

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 А) Тепер створіть папку з ім'ям myproj на електронному диску вашого жорсткого диска та створіть новий

папку із зображеннями імен та розмістіть деякі зображення у папці із зображеннями (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

Ви можете, будь ласка, підказати мені, як зробити те ж саме для динамічних значень. Я маю на увазі, що я хочу записати дані (xml) у свій локальний каталог або і та прочитати їх на своїй jsp-сторінці. Чи є якийсь спосіб записати на сервер вдалося до каталогу, щоб я отримав доступ до них за вищевказаною процедурою ??
Судип7

хоча я можу правильно запустити файл 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файлу.

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


-1

Я зробив це ще простіше. Проблема: CSS-файл містив посилання url на папку img. Отримує 404.

Я переглянув url, http: // tomcatfolder: port / img / blablah.png , якого не існує. Але це насправді вказує на додаток ROOT у Tomcat.

Тому я просто скопіював папку img з мого веб-сайту в цю програму ROOT. Працює!

Звичайно, не рекомендується для виробництва, але це для внутрішнього додатка для розробників інструментів.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.