Самый простой способ обслуживания статических данных извне сервера приложений в веб-приложении Java
У меня есть веб-приложение Java работает на Tomcat. Я хочу загрузить статические изображения, которые будут отображаться как в веб-интерфейсе, так и в PDF-файлах, созданных приложением. Новые изображения будут добавлены и сохранены загрузки через веб-интерфейс.
Это не проблема, чтобы сделать это, имея статические данные, хранящиеся в веб-контейнере, но хранение и загрузка их из-за пределов веб-контейнера дает мне головную боль.
Я бы предпочел не использовать отдельный веб-сервер как Apache для обслуживания статических данных на данный момент. Мне также не нравится идея хранения изображений в двоичном формате в базе данных.
Я видел некоторые предложения, такие как каталог изображений, являющийся символической ссылкой, указывающей на каталог за пределами веб-контейнера, но будет ли этот подход работать как в Windows, так и в средах *nix?
некоторые предлагают написать фильтр или сервлет для обработки изображения, но эти предложения были очень расплывчатыми и высокого уровня без указателей на более подробную информацию о том, как это сделать.
10 ответов
я видел некоторые предложения, такие как каталог изображений, являющийся символической ссылкой, указывающей на каталог за пределами веб-контейнера, но будет ли этот подход работать как в Windows, так и в средах *nix?
если вы придерживаетесь правил пути к файловой системе *nix (т. е. вы используете исключительно косые черты, как в /path/to/files
), то он будет работать на Windows, а также без необходимости возиться с уродливым File.separator
string-конкатенации. Это однако сканироваться следует только на том же рабочем диске, откуда была вызвана эта команда. Поэтому, если Tomcat, например, установлен на C:
тут /path/to/files
фактически указывает на C:\path\to\files
.
если все файлы расположены за пределами webapp, и вы хотите иметь Tomcat's DefaultServlet
чтобы справиться с ними, то все, что вам в основном нужно сделать в Tomcat, это добавить следующий элемент контекста в /conf/server.xml
внутри <Host>
tag:
<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()
by ResultSet#getInputStream()
.
надеюсь, что это помогает.
Читайте также:
- рекомендуемый способ сохранения загруженных файлов в приложении сервлета
- абстрактный шаблон для сервлета статического ресурса (поддержка HTTP-кэша)
- как получить и отобразить изображения из базы данных на странице JSP?
- как передавать аудио / видео файлы, такие как MP3, МР4, AVI и т. д. С помощью сервлета
вы можете сделать это, поставив свои изображения на фиксированный путь (например: /var / images, или c:\images), добавьте параметр в настройках приложения(представлен в моем примере настройками.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 под этим создайте простой веб.в 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 на диске вашего жесткого диска и создайте новый
папка с названием изображения и некоторые изображения в папке images (e:myproj\images\)
предположим myfoto.jpg является помещенных под e:\myproj\images\myfoto.jpg
4 B) Теперь создайте папку с именем WEB-INF в e:\myproj\WEB-INF
и создать сеть.xml в папке WEB-INF
код в web.в XML
<web-app>
</web-app>
Шаг 5:
теперь создать .html-документ с индексом имени.html и место под e:\myproj
код под индексом.формат html Добро пожаловать иметь вид myproj
структура каталогов для вышеуказанного шага 4 и шага 5 выглядит следующим образом
E:\myproj
|--index.html
|
|--images
| |----myfoto.jpg
|
|--WEB-INF
| |--web.xml
Шаг 6:
теперь запустите сервер Apache tomcat
Шаг 7:
откройте браузер и введите url следующим образом
http://localhost:8080/myproj
затем u отображает содержимое, которое предоставляется в индексе.HTML-код
Шаг 8:
для доступа к изображениям под локальным жестким диском (за пределами webroot)
http://localhost:8080/myproj/images/myfoto.jpg
добавить на сервер.XML-код :
<Context docBase="c:/dirtoshare" path="/dir" />
включить параметр списка файлов dir в интернете.XML-код :
<init-param>
<param-name>listings</param-name>
<param-value>true</param-value>
</init-param>
это история с моего рабочего места:
- Мы пытаемся загрузить умножить изображения и файлы документов, используя стойки 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
если кто-то не в состоянии решить свою проблему с принятым ответом, то обратите внимание на следующие соображения:
- нет необходимости упоминать
localhost:<port>
с .
прочитайте InputStream файла и напишите его в ServletOutputStream
для отправки двоичных данных клиенту.
- локальный файл вы можете прочитать файл напрямую, используя FileInputStream ('путь / изображение.png').
- файл базы данных Mongo вы можете получить 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://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>
Если вы хотите работать с 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
Я сделал это еще проще. Проблема: файл CSS имел url-ссылки на папку img. Получает 404.
Я посмотрел на url,http://tomcatfolder:port/img/blablah.формат PNG, которого не существует. Но это действительно указывает на корневое приложение в Tomcat.
поэтому я просто скопировал папку img из моего webapp в это корневое приложение. Работает!
Не рекомендуется для производства, конечно, но это для внутреннего инструмента dev приложения.