Я создаю пользовательский компонент, который является программой просмотра изображений для данного номера продуктов. Я получаю доступ к этим файлам с помощью измененной версии ImageServlet BalusC:
@WebServlet(name="ImageLoader", urlPatterns={"/ImageLoader"})
public class ImageLoader extends HttpServlet {
private static final int DEFAULT_BUFFER_SIZE = 10240; // 10KB.
private static String imagePath = "\\\\xxx.xxx.x.x\\root\\path\\to\\images\\";
/**
* This code is a modified version of the ImageServlet found at balusc.blogspot.com.
* It expects the parameters id and n.
*
* - id: the product number
* - n: the image number to load.
*/
public void goGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String productNumber = URLDecoder.decode(request.getParameter("id"),"utf-8");
String img = URLDecoder.decode(request.getParameter("n"),"utf-8");
if (productNumber == null || img == null) {
response.sendError(HttpServletResponse.SC_NOT_FOUND); // 404.
return;
}
String path = generatePath(productNumber);
File image = new File(generatePath(productNumber), generateImageName(img));
// Check if file actually exists in filesystem.
if (!image.exists()) {
response.sendError(HttpServletResponse.SC_NOT_FOUND); // 404.
return;
}
String contentType = getServletContext().getMimeType(image.getName());
if (contentType == null || !contentType.startsWith("image")) {
response.sendError(HttpServletResponse.SC_NOT_FOUND); // 404.
return;
}
// Init servlet response.
response.reset();
response.setBufferSize(DEFAULT_BUFFER_SIZE);
response.setContentType(contentType);
response.setHeader("Content-Length", String.valueOf(image.length()));
response.setHeader("Content-Disposition", "inline; filename=\"" + image.getName() + "\"");
// Prepare streams.
BufferedInputStream input = null;
BufferedOutputStream output = null;
try {
// Open streams.
input = new BufferedInputStream(new FileInputStream(image), DEFAULT_BUFFER_SIZE);
output = new BufferedOutputStream(response.getOutputStream(), DEFAULT_BUFFER_SIZE);
// Write file contents to response.
byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
int length;
while ((length = input.read(buffer)) > 0) {
output.write(buffer, 0, length);
}
} finally {
close(output);
close(input);
}
}
private String generateImageName(String n) {
int imageNum = Integer.parseInt(n);
StringBuilder ret = new StringBuilder("img-");
if (imageNum < 10) {
ret.append("00");
}
else if(imageNum < 100) {
ret.append("0");
}
ret.append(n);
ret.append(".jpg");
return ret.toString();
}
public static String generatePath(String productNumber) {
Long productNumberLng = Long.parseLong(productNumber);
StringBuilder ret = new StringBuilder(imagePath);
Long thousandPath = productNumberLng - (productNumberLng % 1000);
ret.append(thousandPath);
ret.append("s\\");
ret.append(productNumber);
ret.append("\\");
ret.append(productNumber);
ret.append("\\");
return ret.toString();
}
private static void close(Closeable resource) {
if (resource != null) {
try {
resource.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
Затем я создал составной компонент:
-
Как Вы видите, я просто захватываю список изображений от управляемого компонента. Единственная причина это действительно необходимо, состоит в том, потому что я должен знать, сколько изображения там для данного продукта. Это может варьироваться значительно (где угодно от 8 до 100). Вот то, что код:
@ManagedBean
@RequestScoped
public class ImageLoaderUtilBean {
@ManagedProperty(value = "#{param.id}")
private Long productNumber;
private List images;
public List getImages() {
if (images == null) {
setImages(findImages());
}
return images;
}
public void setImages(List images) {
this.images = images;
}
public Long getProductNumber() {
return productNumber;
}
public void setProductNumber(Long productNumber) {
this.productNumber = productNumber;
}
private List findImages() {
FilenameFilter jpegFilter = new FilenameFilter() {
@Override
public boolean accept(File directory, String filename) {
return filename.toLowerCase().endsWith(".jpg");
}
};
File directory = new File(ImageLoader.generatePath(productNumber.toString()));
if (!directory.exists()) {
return new ArrayList();
}
File[] files = directory.listFiles(jpegFilter);
List ret = new ArrayList();
for (int i = 1; i <= files.length; i++) {
EvfImage img = new EvfImage();
img.setName("file.getName()");
img.setUrl("/ImageLoader?id=" + productNumber + "&n=" + i);
ret.add(img);
}
return ret;
}
}
Существует простой объект для содержания данных, которых я выполняю итерации:
public class EvfImage {
private String url;
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
}
Наконец, я тестирую этот составной компонент с помощью URL http://localhost:8080/project-name/testImages.xhtml?id=213123. Вот код для testImages.xhtml:
Facelet Title
Вот проблема: единственная точка взаимодействия между приложением и составным компонентом должна быть тегом
. Однако это - текучая абстракция. Управляемому компоненту дают номер продуктов на основе запроса id
параметр. Это очень нежелательно. Это создает намного более трудную связь между компонентом и приложением, которое использует его. Идеально, я должен использовать тег следующим образом:
. Однако я не могу выяснить способ сделать это и все еще знать, сколько изображений я должен создать.
Заранее спасибо, Zack
Править: Было бы совершенно приемлемо назвать сервлет, который берет в номере продуктов и возвращает количество изображений, которые имеет тот продукт. Однако я должен все же найти способ выполнить цикл n
времена (для цикла) в противоположность выполнению его однажды для каждого объекта в Наборе (цикл foreach). Я в значительной степени доволен любым решением, которое включает удаление это @ManagedProperty("#{param.id}")
от отступающего боба.
Замена @ManagedProperty(value="#{param. id}")
в ImageLoaderUtilBean
будет
<sdCom:imageViewer listID="test" productNumber="#{param.id}" />
в сочетании со следующим в cc:implementation
before ui:repeat
:
<c:set target="#{imageLoaderUtilBean}" property="productNumber" value="#{cc.attrs.productNumber}" />
Где c:
- это встроенная JSTL библиотека Facelets, которая должна быть объявлена следующим образом:
xmlns:c="http://java.sun.com/jsp/jstl/core"
У Facelets нет замены для c:set
(пока? ).