我正在創建一個自定義組件,該組件是給定產品編號的圖像查看器。我訪問使用BalusC的ImageServlet的修改版本這些文件:JSF 2 cc:將屬性傳遞到後臺bean
@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.
* <ul>
* <li><b>id:</b> the product number</li>
* <li><b>n:</b> the image number to load.</li>
*/
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();
}
}
}
}
接下來,我創建了一個複合組件:
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:cc="http://java.sun.com/jsf/composite"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<!-- INTERFACE -->
<cc:interface>
<cc:attribute name="productNumber" shortDescription="The product number whose images should be displayed."
type="java.lang.Long" />
<cc:attribute name="listID" shortDescription="This ID is the html ID of the <ul> element." />
</cc:interface>
<!-- IMPLEMENTATION -->
<cc:implementation>
<div id="#{cc.clientId}">
<ul id="#{cc.attrs.listID}">
<ui:repeat value="#{imageLoaderUtilBean.images}" var="image">
<li>
<h:graphicImage value="#{image.url}" alt="#{image.name}" />
</li>
</ui:repeat>
</ul>
</div>
</cc:implementation>
</html>
正如你所看到的,我只是從管理抓取的圖像列表豆。這是真正必要的唯一原因是因爲我需要知道給定產品有多少圖像。這可能差別很大(從8到100)。下面是代碼:
@ManagedBean
@RequestScoped
public class ImageLoaderUtilBean {
@ManagedProperty(value = "#{param.id}")
private Long productNumber;
private List<EvfImage> images;
public List<EvfImage> getImages() {
if (images == null) {
setImages(findImages());
}
return images;
}
public void setImages(List<EvfImage> images) {
this.images = images;
}
public Long getProductNumber() {
return productNumber;
}
public void setProductNumber(Long productNumber) {
this.productNumber = productNumber;
}
private List<EvfImage> 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<EvfImage>();
}
File[] files = directory.listFiles(jpegFilter);
List<EvfImage> ret = new ArrayList<EvfImage>();
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;
}
}
最後,我使用的http://localhost:8080/project-name/testImages.xhtml?id=213123一個URL測試該複合部件。下面是testImages.xhtml的代碼:
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:sdCom="http://java.sun.com/jsf/composite/components/sd">
<h:head>
<title>Facelet Title</title>
</h:head>
<h:body>
<sdCom:imageViewer listID="test" />
</h:body>
</html>
這裏的問題是:在應用程序和複合組件之間的交互的唯一的點應該是標籤<sdCom:imageViewer listID="test" />
。但是,這是一個抽象漏洞。託管bean根據請求的參數id
給出產品編號。這是非常不可取的。它在組件和正在使用它的應用程序之間創建更緊密的耦合。理想情況下,我應該使用如下標籤:<sdCom:imageViewer listID="test" productNumber="213123"/>
。但是,我無法想出一個辦法來做到這一點,仍然知道我需要創建多少圖片。
由於提前, 扎克
編輯:這將是完全可以接受的調用一個servlet這需要在產品編號,並返回其該產品具有的圖像的數量。然而,我還沒有找到一種方法來運行一個循環(循環)n
,而不是爲一個集合(foreach循環)中的每個對象運行一次。我非常滿意任何涉及從支持bean中刪除@ManagedProperty("#{param.id}")
的解決方案。
將複合組件添加到可以傳遞任意'EvfImage'數組的另一個屬性不是更好嗎?這樣你的組件就獨立於託管bean。 – Behrang 2010-06-23 21:12:45
Bytecode忍者:謝謝你的迴應。這聽起來像是一個體面的妥協。在我的特殊情況下,它並不像只傳遞一個產品號(在這種情況下),但它也不會繞過組件的接口。除非有人提出更好的建議,否則我會使用你的建議。謝謝! – 2010-06-23 21:22:38
Bytecode Ninja:我如何限制傳入的列表以確保它只允許列出EvfImages? – 2010-06-24 20:35:15