使用 JSF 自定义标签将图像从客户端上传到服务器
我想将图像上传到服务器内的文件夹中。
由于某种原因我不能。我不明白为什么我的过滤器没有被触发。以及为什么文件没有上传。有人可以看一下我的代码并帮助我找到文件未上传的原因吗?
我将粘贴到目前为止我所做的所有操作,以便您帮助我找到错误:
1.将 commons-fileupload-1.2.1.jar 和 commons-io-1.4.jar 添加到 lib 文件夹(自动添加到classpath)
2.创建一个用于制作标签库的 xml可用(放置在WEB-INF文件夹中)
<?xml version="1.0" encoding="UTF-8"?>
<facelet-taglib version="2.0"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-facelettaglibrary_2_0.xsd">
<namespace>http://corejsf.com</namespace>
<tag>
<tag-name>upload</tag-name>
<component>
<component-type>javax.faces.Input</component-type>
<renderer-type>com.corejsf.Upload</renderer-type>
</component>
</tag>
</facelet-taglib>
3.创建一个用于实现标签的包,并将其放入名为com.corejsf的新包中;
这是来源:
package com.corejsf;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import javax.el.ValueExpression;
import javax.faces.FacesException;
import javax.faces.component.EditableValueHolder;
import javax.faces.component.UIComponent;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.faces.render.FacesRenderer;
import javax.faces.render.Renderer;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.fileupload.FileItem;
@FacesRenderer(componentFamily="javax.faces.Input",
rendererType="com.corejsf.Upload")
public class UploadRenderer extends Renderer {
public void encodeBegin(FacesContext context, UIComponent component)
throws IOException {
if (!component.isRendered()) return;
ResponseWriter writer = context.getResponseWriter();
String clientId = component.getClientId(context);
writer.startElement("input", component);
writer.writeAttribute("type", "file", "type");
writer.writeAttribute("name", clientId, "clientId");
writer.endElement("input");
writer.flush();
}
public void decode(FacesContext context, UIComponent component) {
ExternalContext external = context.getExternalContext();
HttpServletRequest request = (HttpServletRequest) external.getRequest();
String clientId = component.getClientId(context);
FileItem item = (FileItem) request.getAttribute(clientId);
Object newValue;
ValueExpression valueExpr = component.getValueExpression("value");
if (valueExpr != null) {
Class<?> valueType = valueExpr.getType(context.getELContext());
if (valueType == byte[].class) {
newValue = item.get();
}
else if (valueType == InputStream.class) {
try {
newValue = item.getInputStream();
} catch (IOException ex) {
throw new FacesException(ex);
}
}
else {
String encoding = request.getCharacterEncoding();
if (encoding != null)
try {
newValue = item.getString(encoding);
} catch (UnsupportedEncodingException ex) {
newValue = item.getString();
}
else
newValue = item.getString();
}
((EditableValueHolder) component).setSubmittedValue(newValue);
((EditableValueHolder) component).setValid(true);
}
Object target = component.getAttributes().get("target");
if (target != null) {
File file;
if (target instanceof File)
file = (File) target;
else {
ServletContext servletContext
= (ServletContext) external.getContext();
String realPath = servletContext.getRealPath(target.toString());
file = new File(realPath);
}
try { // ugh--write is declared with "throws Exception"
item.write(file);
} catch (Exception ex) {
throw new FacesException(ex);
}
}
}
}
4.然后我添加了一个servlet过滤器,以区分拦截请求并将其放在与自定义标签相同的包实现
这是其来源:
package com.corejsf;
import java.io.File;
import java.io.IOException;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
public class UploadFilter implements Filter {
private int sizeThreshold = -1;
private String repositoryPath;
public void init(FilterConfig config) throws ServletException {
repositoryPath = config.getInitParameter(
"com.corejsf.UploadFilter.repositoryPath");
try {
String paramValue = config.getInitParameter(
"com.corejsf.UploadFilter.sizeThreshold");
if (paramValue != null)
sizeThreshold = Integer.parseInt(paramValue);
}
catch (NumberFormatException ex) {
ServletException servletEx = new ServletException();
servletEx.initCause(ex);
throw servletEx;
}
}
public void destroy() {
}
public void doFilter(ServletRequest request,
ServletResponse response, FilterChain chain)
throws IOException, ServletException {
if (!(request instanceof HttpServletRequest)) {
chain.doFilter(request, response);
return;
}
HttpServletRequest httpRequest = (HttpServletRequest) request;
boolean isMultipartContent
= ServletFileUpload.isMultipartContent(httpRequest);
if (!isMultipartContent) {
chain.doFilter(request, response);
return;
}
DiskFileItemFactory factory = new DiskFileItemFactory();
if (sizeThreshold >= 0)
factory.setSizeThreshold(sizeThreshold);
if (repositoryPath != null)
factory.setRepository(new File(repositoryPath));
ServletFileUpload upload = new ServletFileUpload(factory);
try {
@SuppressWarnings("unchecked") List<FileItem> items
= (List<FileItem>) upload.parseRequest(httpRequest);
final Map<String, String[]> map = new HashMap<String, String[]>();
for (FileItem item : items) {
String str = item.getString();
if (item.isFormField())
map.put(item.getFieldName(), new String[] { str });
else
httpRequest.setAttribute(item.getFieldName(), item);
}
chain.doFilter(new
HttpServletRequestWrapper(httpRequest) {
public Map<String, String[]> getParameterMap() {
return map;
}
// busywork follows ... should have been part of the wrapper
public String[] getParameterValues(String name) {
Map<String, String[]> map = getParameterMap();
return (String[]) map.get(name);
}
public String getParameter(String name) {
String[] params = getParameterValues(name);
if (params == null) return null;
return params[0];
}
public Enumeration<String> getParameterNames() {
Map<String, String[]> map = getParameterMap();
return Collections.enumeration(map.keySet());
}
}, response);
} catch (FileUploadException ex) {
ServletException servletEx = new ServletException();
servletEx.initCause(ex);
throw servletEx;
}
}
}
5.然后我在web.xml。 (我想使用注释,但我不知道如何使用,有人知道如何使用注释来做到这一点吗?) 还添加了 corejsf.taglib.xml
<!-- NEEDED FOR FILE UPLOAD -->
<filter>
<filter-name>Upload Filter</filter-name>
<filter-class>com.corejsf.UploadFilter</filter-class>
<init-param>
<param-name>sizeThreshold</param-name>
<param-value>1024</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>Upload Filter</filter-name>
<url-pattern>/faces/upload/*</url-pattern>
</filter-mapping>
<context-param>
<param-name>javax.faces.PROJECT_STAGE</param-name>
<param-value>Development</param-value>
</context-param>
<context-param>
<param-name>facelets.LIBRARIES</param-name>
<param-value>/WEB-INF/corejsf.taglib.xml</param-value>
</context-param>
6.在我的 WebContent 文件夹中,我创建了一个名为 upload 的子文件夹(上传文件的目标)
7.在 jsf 页面内,我使用标签进行上传和提交,并使用托管 bean 方法来创建文件名:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:corejsf="http://corejsf.com">
....
<h:form enctype="multipart/form-data">
<corejsf:upload target="upload/#{placeAddController.prepareUniqueIdentifier}" />
....
<h:commandButton value="Dalje" style=" font-weight: bold; font-size:150%; action="/submittedImage" />
...
</h:form>
还有java ManagedBean:
@ManagedBean
@RequestScoped
public class PlaceAddControler {
…
public String prepareUniqueIdentifier() {
return UUID.randomUUID().toString()+"png";
}
-一切看起来都不错,但有些东西丢失或错误。 你觉得怎么样,为什么不上传?
I would like to upload images onto a folder inside the server.
For some reason i cant. I don't understand why my filter is not being triggered.And why the file does not get uploaded. Could someone have a look at my code and help me find the reason why the files don't get uploaded?
I will paste all i did till now so you can help me find the mistake:
1.Added commons-fileupload-1.2.1.jar and commons-io-1.4.jar to the lib folder(Automatically get added to the classpath)
2.Created an xml that wil make the tag library available(This is placed inside WEB-INF folder)
<?xml version="1.0" encoding="UTF-8"?>
<facelet-taglib version="2.0"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-facelettaglibrary_2_0.xsd">
<namespace>http://corejsf.com</namespace>
<tag>
<tag-name>upload</tag-name>
<component>
<component-type>javax.faces.Input</component-type>
<renderer-type>com.corejsf.Upload</renderer-type>
</component>
</tag>
</facelet-taglib>
3.Create a package for the implementation of the tag and place in a new package called com.corejsf;
Here is the source:
package com.corejsf;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import javax.el.ValueExpression;
import javax.faces.FacesException;
import javax.faces.component.EditableValueHolder;
import javax.faces.component.UIComponent;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.faces.render.FacesRenderer;
import javax.faces.render.Renderer;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.fileupload.FileItem;
@FacesRenderer(componentFamily="javax.faces.Input",
rendererType="com.corejsf.Upload")
public class UploadRenderer extends Renderer {
public void encodeBegin(FacesContext context, UIComponent component)
throws IOException {
if (!component.isRendered()) return;
ResponseWriter writer = context.getResponseWriter();
String clientId = component.getClientId(context);
writer.startElement("input", component);
writer.writeAttribute("type", "file", "type");
writer.writeAttribute("name", clientId, "clientId");
writer.endElement("input");
writer.flush();
}
public void decode(FacesContext context, UIComponent component) {
ExternalContext external = context.getExternalContext();
HttpServletRequest request = (HttpServletRequest) external.getRequest();
String clientId = component.getClientId(context);
FileItem item = (FileItem) request.getAttribute(clientId);
Object newValue;
ValueExpression valueExpr = component.getValueExpression("value");
if (valueExpr != null) {
Class<?> valueType = valueExpr.getType(context.getELContext());
if (valueType == byte[].class) {
newValue = item.get();
}
else if (valueType == InputStream.class) {
try {
newValue = item.getInputStream();
} catch (IOException ex) {
throw new FacesException(ex);
}
}
else {
String encoding = request.getCharacterEncoding();
if (encoding != null)
try {
newValue = item.getString(encoding);
} catch (UnsupportedEncodingException ex) {
newValue = item.getString();
}
else
newValue = item.getString();
}
((EditableValueHolder) component).setSubmittedValue(newValue);
((EditableValueHolder) component).setValid(true);
}
Object target = component.getAttributes().get("target");
if (target != null) {
File file;
if (target instanceof File)
file = (File) target;
else {
ServletContext servletContext
= (ServletContext) external.getContext();
String realPath = servletContext.getRealPath(target.toString());
file = new File(realPath);
}
try { // ugh--write is declared with "throws Exception"
item.write(file);
} catch (Exception ex) {
throw new FacesException(ex);
}
}
}
}
4.Then I added a servlet filter, to distinguish to intercept the requests and placed it in the same package as the custom tag implementation
This is its source:
package com.corejsf;
import java.io.File;
import java.io.IOException;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
public class UploadFilter implements Filter {
private int sizeThreshold = -1;
private String repositoryPath;
public void init(FilterConfig config) throws ServletException {
repositoryPath = config.getInitParameter(
"com.corejsf.UploadFilter.repositoryPath");
try {
String paramValue = config.getInitParameter(
"com.corejsf.UploadFilter.sizeThreshold");
if (paramValue != null)
sizeThreshold = Integer.parseInt(paramValue);
}
catch (NumberFormatException ex) {
ServletException servletEx = new ServletException();
servletEx.initCause(ex);
throw servletEx;
}
}
public void destroy() {
}
public void doFilter(ServletRequest request,
ServletResponse response, FilterChain chain)
throws IOException, ServletException {
if (!(request instanceof HttpServletRequest)) {
chain.doFilter(request, response);
return;
}
HttpServletRequest httpRequest = (HttpServletRequest) request;
boolean isMultipartContent
= ServletFileUpload.isMultipartContent(httpRequest);
if (!isMultipartContent) {
chain.doFilter(request, response);
return;
}
DiskFileItemFactory factory = new DiskFileItemFactory();
if (sizeThreshold >= 0)
factory.setSizeThreshold(sizeThreshold);
if (repositoryPath != null)
factory.setRepository(new File(repositoryPath));
ServletFileUpload upload = new ServletFileUpload(factory);
try {
@SuppressWarnings("unchecked") List<FileItem> items
= (List<FileItem>) upload.parseRequest(httpRequest);
final Map<String, String[]> map = new HashMap<String, String[]>();
for (FileItem item : items) {
String str = item.getString();
if (item.isFormField())
map.put(item.getFieldName(), new String[] { str });
else
httpRequest.setAttribute(item.getFieldName(), item);
}
chain.doFilter(new
HttpServletRequestWrapper(httpRequest) {
public Map<String, String[]> getParameterMap() {
return map;
}
// busywork follows ... should have been part of the wrapper
public String[] getParameterValues(String name) {
Map<String, String[]> map = getParameterMap();
return (String[]) map.get(name);
}
public String getParameter(String name) {
String[] params = getParameterValues(name);
if (params == null) return null;
return params[0];
}
public Enumeration<String> getParameterNames() {
Map<String, String[]> map = getParameterMap();
return Collections.enumeration(map.keySet());
}
}, response);
} catch (FileUploadException ex) {
ServletException servletEx = new ServletException();
servletEx.initCause(ex);
throw servletEx;
}
}
}
5.Then I registered the filter in the web.xml. (I wanted to use an annotation but I didn’t know how, does someon know how can I do that with an annotation?)
Also added the corejsf.taglib.xml
<!-- NEEDED FOR FILE UPLOAD -->
<filter>
<filter-name>Upload Filter</filter-name>
<filter-class>com.corejsf.UploadFilter</filter-class>
<init-param>
<param-name>sizeThreshold</param-name>
<param-value>1024</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>Upload Filter</filter-name>
<url-pattern>/faces/upload/*</url-pattern>
</filter-mapping>
<context-param>
<param-name>javax.faces.PROJECT_STAGE</param-name>
<param-value>Development</param-value>
</context-param>
<context-param>
<param-name>facelets.LIBRARIES</param-name>
<param-value>/WEB-INF/corejsf.taglib.xml</param-value>
</context-param>
6.On my WebContent folder I created a subfolder called upload(Destination of the uploaded files)
7.Inside a jsf page I use the tag for upload and submit and also use a managed bean method to create the file names:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:corejsf="http://corejsf.com">
....
<h:form enctype="multipart/form-data">
<corejsf:upload target="upload/#{placeAddController.prepareUniqueIdentifier}" />
....
<h:commandButton value="Dalje" style=" font-weight: bold; font-size:150%; action="/submittedImage" />
...
</h:form>
And the java managedbean:
@ManagedBean
@RequestScoped
public class PlaceAddControler {
…
public String prepareUniqueIdentifier() {
return UUID.randomUUID().toString()+"png";
}
-All seems ok, but something is missing or wrong.
What do you think, why is not uploading?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
过滤器显然没有被调用。在
doFilter()
方法上放置调试断点,或者添加 Logger 语句或穷人的System.out.println()
语句,以了解哪些代码被准确执行、哪些没有执行以及哪些代码被执行变量已准确设置。仅当请求 URL 与过滤器的
匹配时才会调用过滤器。它需要与您在 JSF 页面的浏览器地址栏中看到的请求 URL 的 URL 模式与上传表单相匹配。由于您已配置 URL 模式/faces/upload/*
,因此仅当请求 URL 类似于以下内容时才会调用它关于如何注释过滤器的问题,请使用
@WebFilter
。与问题无关,代码中存在一些缺陷(是的,我知道,大多数不是你的,我只是想警告你):
此过滤器不支持带有在表单中使用多选或多复选框时可以获得多个值,例如
foo=val1&foo=val2&foo=val3
。只有最后选择/检查的值才会以这种方式出现在参数映射中。我建议相应地修复过滤器代码。如果您想要永久存储,将上传的文件存储在 webcontent 文件夹中是没有用的。每当您重新部署 webapp WAR/EAR 文件时,原来展开的 webapp 文件夹将被完全删除,包括 webapp 运行时添加的文件。 Web 服务器不会保留新展开的 webapp 文件夹中的更改。如果您想要更永久的存储,则应该将文件存储在 webapp 文件夹之外,最好是绝对路径。例如
/var/webapp/upload
。另请参阅:
The filter is apparently not been invoked. Put debug breakpoints on the
doFilter()
method or add Logger statements or poor man'sSystem.out.println()
statements to learn what code exactly get executed and what not and what variables exactly are been set.The filter will only be invoked when the request URL matches the filter's
<url-pattern>
. It needs to match the URL pattern of the request URL as you see in the browser address bar of the JSF page with the upload form. As you have configured the URL pattern,/faces/upload/*
, it will only be invoked when the request URL look like something thisAs to the question how to annotate the filter, use
@WebFilter
.Unrelated to the problem, there are some flaws in the code (yes, I know, the majority is not yours, I just want to warn you):
This filter does not support request parameters with multiple values like
foo=val1&foo=val2&foo=val3
as you can get when multi-select or multi-checkbox are been used in the forms. Only the last selected/checked value ends up in the parameter map this way. I'd recommend to fix the filter code accordingly.Storing uploaded files in webcontent folder is not useful if you want a permanent storage. Whenever you redeploy the webapp WAR/EAR file, then the originally expanded webapp folder will be entirely deleted, including the files which were been added during webapp's runtime. The webserver don't retain the changes in the freshly expanded webapp folder. If you want a more permanent storage, you should be storing the files outside the webapp folder, preferably on an absolute path. E.g.
/var/webapp/upload
.See also: