Самый простой способ обслуживания статических данных извне сервера приложений в веб-приложении 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()
,
Надеюсь это поможет.
Смотрите также:
- Рекомендуемый способ сохранения загруженных файлов в приложении сервлета
- Абстрактный шаблон для сервлета статических ресурсов (с поддержкой HTTP-кэша)
- Как получить и отобразить изображения из базы данных на странице JSP?
- Как транслировать аудио / видео файлы, такие как MP3, MP4, AVI и т. Д., Используя сервлет
Вы можете сделать это, поместив ваши изображения по фиксированному пути (например: /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
для отправки двоичных данных клиенту.
- Локальный файл Вы можете прочитать файл напрямую, используя FileInputStream ('path / image.png').
- Mongo DataBase файл вы можете получить InputStream, используя GridFS.
@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>
Если кто-то не может решить свою проблему с помощью принятого ответа, обратите внимание на следующие соображения:
- не нужно упоминать
localhost:<port>
с<img> src
приписывать. - убедитесь, что вы запускаете этот проект за пределами 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. Работает!
Конечно, не рекомендуется для производства, но это для внутреннего приложения разработчика.