Загрузка изображения на Java / JSP.Где хранить эти файлы изображений?

StackOverflow https://stackoverflow.com/questions/385332

  •  23-08-2019
  •  | 
  •  

Вопрос

Я пишу простое приложение, которое позволяет пользователю загружать изображения.После загрузки пользователь может пометить их тегом или удалить.

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

Я использую Java / JSP (в частности, Stripes framework, но моя проблема носит общий характер).

Мой вопрос в том, где мне хранить эти файлы изображений после их загрузки?

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

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

Похоже, что Tomcat не выбирает вновь загруженные изображения автоматически.

Есть ли у кого-нибудь какие-нибудь решения?

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

Спасибо.

Это было полезно?

Решение

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

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

Другие советы

Однако хранить загруженные изображения внутри каталога веб-приложения неразумно, и вы это знаете.

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

Я решал эту проблему по-разному.

Во-первых, непереносимый способ заключается в том, что Glassfish (и, я полагаю, Tomcat тоже) позволяет вам сопоставлять внешний каталог с иерархией webapps.Это работает действительно хорошо и делает именно то, что вы хотите.Это позволяет вам хранить ваши изображения во внешнем каталоге вдали от вашего веб-приложения, но при этом обслуживать их.

Однако этот метод не является портативным.

Способ, которым я сделал это переносимо, - это создать фильтр.

Вы размещаете фильтр в каком-нибудь очевидном месте, скажем "/images".

Что делает фильтр, так это:

  • он проверяет наличие изображения (или чего-либо еще, он работает с любым статическим ресурсом) в специальном каталоге внутри веб-приложения.Для нашего примера мы будем использовать URL-адрес "/webapp/images".

  • если файл НЕ существует, мы копируем файл из вашего внешнего расположения в соответствующее место в веб-приложении.Итак, допустим, что самый требуемый URL-адрес - "/images/banner.gif".И что ваши файлы хранятся на диске по адресу "/ home/ app/images".Итак, наш исходный файл - "/home/app/images/banner.gif".Затем мы копируем его туда, где мы хотим, в дереве веб-приложений.Для этого мы используем "ServletContext.getRealPath".Таким образом, местом назначения будет "ServletContext.get RealPath("/webapp/images/banner.gif").Просто скопируйте исходный код в пункт назначения.

  • если файл уже существовал или существует сейчас, просто перейдите к реальному изображению по адресу /webapp/images/banner.gif.

Фактически, в конечном итоге у вас будет файловый кэш в дереве развертывания webapps.Недостатком является то, что это кэш, поэтому его необходимо поддерживать (т.е.вы должны проверить, является ли оригинал более новым, чем ваш кэш, убедитесь, что вы удалили, если источник удален, и т.д.).Кроме того, он дублирует ваши ресурсы, поэтому ваши изображения в конечном итоге будут занимать в два раза больше дискового пространства.Наконец, есть начальная стоимость копирования при запуске.

Однако это ДЕЙСТВИТЕЛЬНО работает, и это избавляет вас от необходимости обслуживать статические ресурсы, используя ваш собственный код.(Что является третьим решением, сопоставьте фильтр / сервлет для перехвата URL-адресов и просто передайте его самостоятельно.)

Я бы посмотрел на конструкцию внутри Tomcat (предполагая, что она существует), чтобы выполнить сопоставление для вас.Я знаю, что это существует в Glassfish.(Погуглите alternatedocroot для Glassfish, чтобы посмотреть, как это работает.)

Я использовал два веб-приложения, чтобы избежать перезаписи загруженных изображений в случае повторного развертывания нового файла main application war.

Но, как вы упомянули, нет другого варианта, кроме как передавать их через Сервлет или что-то еще, я думаю, я могу сохранить их вне каталога tomcat.

Я хотел избежать написания этого потокового сервлета.Просто слишком маленький проект, чтобы справиться со всем этим беспорядком (например, с правильным типом контента, 404 и т.д.) При написании потокового сервлета.

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Image streaming Servlet.
 */
public class ImageDisplayServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    /**
     * @see HttpServlet#HttpServlet()
     */
    public ImageDisplayServlet() {
        super();
    }

    /**
     * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String relativePath = trimToEmpty(request.getPathInfo());

        // Make sure no one try to screw with us. 
        // This is important as user can literally access any file if we are not careful
        if(isXSSAttack(relativePath) == false) {
            String pathToFile = this.getServletContext().getRealPath(request.getPathInfo());
            File file = new File(pathToFile);

            System.out.println("Looking for file " + file.getAbsolutePath());

            // show a 404 page
            if(!file.exists() || !file.isFile()) {
                httpError(404, response);
            } else {
                try {
                    streamImageFile(file, response);
                } catch(Exception e) {
                    // Tell the user there was some internal server error.\
                    // 500 - Internal server error.
                    httpError(500, response);
                    e.printStackTrace();
                }
            }
        } else {
            // what to do if i think it is a XSS attack ?!?
        }
    }

    private void streamImageFile(File file, HttpServletResponse response) {
        // find the right MIME type and set it as content type
        response.setContentType(getContentType(file));
        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;
        try {
            response.setContentLength((int) file.length());

            // Use Buffered Stream for reading/writing.
            bis = new BufferedInputStream(new FileInputStream(file));
            bos = new BufferedOutputStream(response.getOutputStream());

            byte[] buff = new byte[(int) file.length()];
            int bytesRead;

            // Simple read/write loop.
            while (-1 != (bytesRead = bis.read(buff, 0, buff.length))) {
                bos.write(buff, 0, bytesRead);
            }
        } catch (Exception e) {

            throw new RuntimeException(e);
        } finally {
            if (bis != null) {
                try {
                    bis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                    // To late to do anything about it now, we may have already sent some data to user.
                }
            }
            if (bos != null) {
                try {
                    bos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                    // To late to do anything about it now, we may have already sent some data to user.
                }
            }
        } 
    }

    private String getContentType(File file) {
        if(file.getName().length() > 0) {
            String[] parts = file.getName().split("\\.");
            if(parts.length > 0) {
                // only last part interests me
                String extention = parts[parts.length - 1];
                if(extention.equalsIgnoreCase("jpg")) {
                    return "image/jpg";
                } else if(extention.equalsIgnoreCase("gif")) {
                    return "image/gif"; 
                } else if(extention.equalsIgnoreCase("png")) {
                    return "image/png";
                }
            }
        }
        throw new RuntimeException("Can not find content type for the file " +  file.getAbsolutePath());
    }

    private String trimToEmpty(String pathInfo) {
        if(pathInfo == null) {
            return "";
        } else {
            return pathInfo.trim();
        }
    }

    private void httpError(int statusCode, HttpServletResponse response) {
        try {
            response.setStatus(statusCode);
            response.setContentType("text/html");
            PrintWriter writer = response.getWriter();
            writer.append("<html><body><h1>Error Code: " + statusCode + "</h1><body></html>");
            writer.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private boolean isXSSAttack(String path) {
        boolean xss = false;
        // Split on the bases of know file separator
        String[] parts = path.split("/|\\\\");

        // Now verify that no part contains anything harmful
        for(String part : parts) {
            // No double dots .. 
            // No colons :
            // No semicolons ;
            if(part.trim().contains("..") || part.trim().contains(":") || part.trim().contains(";")) {
                // Fire in the hole!
                xss = true;
                break;
            }
        }
        return xss;
    }

    /**
     * @see HttpServlet#doPost(Ht/promotions/some.jpgtpServletRequest request, HttpServletResponse response)
     */
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }
}

Хорошо, вот сервлет, который я быстро написал, который может передавать изображения в потоковом режиме:

Вот список ограничений и известных проблем:

  • Может иметь уязвимость XSS, используйте с осторожностью
  • Не готовое к производству использование в качестве эталона
  • Изображения должны находиться в каталоге веб-приложения.Можно легко изменить, но я слишком ленив (оно того не стоит, проект слишком маленький)
  • Транслируйте только файлы jpg, gif или png.

Использование:

Допустим, вы развертываете это веб-приложение под названием images как отдельное приложение.

http://www.example.com/images/promotions/promo.jpg

означает, что в разделе "рекламные акции" должен быть каталог с изображением "promo.jpg" с в этом веб-приложении images.

PS:Не спрашивайте, почему я делаю это единственное решение для контейнера сервлетов, которое отнимает много времени.

  <servlet>
    <description></description>
    <display-name>ImageDisplayServlet</display-name>
    <servlet-name>ImageDisplayServlet</servlet-name>
    <servlet-class>com.example.images.ImageDisplayServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>ImageDisplayServlet</servlet-name>
    <url-pattern>/*</url-pattern>
  </servlet-mapping>

О, да, настройте свой Сервлет, как указано выше, для достижения наилучших результатов: P

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top