I have consulted most of the posts here on stackoverflow on uploading images in primefaces. With this help, I have been able to upload an image to a destination path statically specified in code as shown in this post. save image file in specific directory jsf primefaces project. This works fine.
However, I wish to upload an Image to a desitnation path specified in web.xml. This is because I want the path to be configurable even after the application is deployed. When I use ServletContext#getRealpath(), the return path is with in the myProject folder, but I want the destination path to be completely external to the project since I have found it as the best way. e.g E:/myUploads
This is my web.xml
<filter>
<filter-name>PrimeFaces FileUpload Filter</filter-name>
<filter-class>org.primefaces.webapp.filter.FileUploadFilter</filter-class>
<init-param>
<param-name>thresholdSize</param-name>
<param-value>51200</param-value>
</init-param>
<init-param>
<param-name>uploadDirectory</param-name>
<param-value>E:/myUploads</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>PrimeFaces FileUpload Filter</filter-name>
<servlet-name>Faces Servlet</servlet-name>
</filter-mapping>
This is my bean.
public void handleFileUpload(FileUploadEvent event){
//get uploaded file from the event
UploadedFile uploadedFile = (UploadedFile) event.getFile();
//create an InputStream from the uploaded file
InputStream inputStr = null;
try
{
inputStr = uploadedFile.getInputstream();
} catch (IOException e) {
//log error
}
ServletContext servletContext = (ServletContext)FacesContext.getCurrentInstance().getExternalContext().getContext();
String uploadPath = servletContext.getRealPath("");
File destFile = new File(uploadPath);
//use org.apache.commons.io.FileUtils to copy the File
try {
FileUtils.copyInputStreamToFile(inputStr, destFile);
} catch (IOException e) {
//log error
}
FacesMessage msg = new FacesMessage(event.getFile().getFileName() + " is uploaded.");
FacesContext.getCurrentInstance().addMessage(null, msg);
}
My desire is to save the Uploaded Image in E:/myUploads without having to say:
String destPath = "E:\\myUploads\\" + uploadedFile.getFileName();
I will be glad if you also show me how to display the uploaded images using
<p:graphicImage
The uploadDirectory initialization parameter represents the temporary file storage location for the case the uploaded file is larger than the configured threshold size. The configured directory is not intented as a permanent file storage location. It will be auto-cleaned at moments beyond your control.
Get rid of it altogether and create an independent context parameter instead.
<context-param>
<param-name>uploadDirectory</param-name>
<param-value>E:/myUploads</param-value>
</context-param>
Then you can use
String directory = externalContext.getInitParameter("uploadDirectory");
String filename = FilenameUtils.getName(uploadedFile.getFileName());
File file = new File(directory, filename);
// ...
You should indeed never never use getRealPath(). You've by the way another potential problem when another user happens to upload a file with coincidentally the same filename. You can use File#createTempFile() to autogenerate unique filenames with a fixed prefix/suffix. See also How to save uploaded file in JSF.
BalusC Answer has helped me a great deal.. Thanks again BalusC. If any one is interested in the final code, here it is...
Am using tools Netbeans 7.4, Primefaces 4.0, GlassFish 3.1.2
This is my upload Form.
<h:form enctype="multipart/form-data">
<p:fileUpload fileUploadListener="#{fileUploadController.handleFileUpload}" mode="advanced" dragDropSupport="false"
update="messages" sizeLimit="100000" fileLimit="3" allowTypes="/(\.|\/)(gif|jpe?g|png)$/" />
<p:messages id="messages" showDetail="true"/>
</h:form>
I have the Apache Commons libraries in my lib
commons-io-2.4.jar (http://commons.apache.org/io)
commons-fileupload-1.3.jar (http://commons.apache.org/fileupload)
I have this in my web.xml
<filter>
<filter-name>PrimeFaces FileUpload Filter</filter-name>
<filter-class>org.primefaces.webapp.filter.FileUploadFilter</filter-class>
<init-param>
<param-name>thresholdSize</param-name>
<param-value>51200</param-value>
</init-param>
</filter>
<context-param>
<param-name>uploadDirectory</param-name>
<param-value>E:/myUploads</param-value>
</context-param>
<filter-mapping>
<filter-name>PrimeFaces FileUpload Filter</filter-name>
<servlet-name>Faces Servlet</servlet-name>
</filter-mapping>
This is my FileUploadController
#ManagedBean(name = "fileUploadController")
#ViewScoped
public class FileUploadController {
public void handleFileUpload(FileUploadEvent event) {
//get uploaded file from the event
UploadedFile uploadedFile = (UploadedFile) event.getFile();
//create an InputStream from the uploaded file
InputStream inputStr = null;
try {
inputStr = uploadedFile.getInputstream();
} catch (IOException e) {
//log error
}
ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
String directory = externalContext.getInitParameter("uploadDirectory");
String filename = FilenameUtils.getName(uploadedFile.getFileName());
File destFile = new File(directory, filename);
//use org.apache.commons.io.FileUtils to copy the File
try {
FileUtils.copyInputStreamToFile(inputStr, destFile);
} catch (IOException e) {
//log error
}
FacesMessage msg = new FacesMessage(event.getFile().getFileName() + " is uploaded.");
FacesContext.getCurrentInstance().addMessage(null, msg);
}
}
This works well. All the best.
Related
I have a Struts1 application and am unsuccessfully trying to get a filter to work in order to add headers/etc to the response after the action has completed, but am not able to get it to work.
By the time the struts action is completed and control is returned to my filter, the response is already committed.
web.xml:
<filter>
<filter-name>workflow</filter-name>
<filter-class>webapp.session.WorkflowModifierFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>workflow</filter-name>
<servlet-name>action</servlet-name>
</filter-mapping>
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
<init-param>
<param-name>config</param-name>
<param-value>/WEB-INF/struts-config.xml</param-value>
</init-param>
<init-param>
<param-name>debug</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>detail</param-name>
<param-value>2</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
My filter is the following:
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
if( !servletResponse.isCommitted() )
log.debug("Not Committed! Can modify it!");
filterChain.doFilter(servletRequest, servletResponse);
// depending on the response, I'd like to add headers here
if( servletResponse.getStatus() == 200 )
servletResponse.addIntHeader( "x-success", 1 );
if( servletResponse.isCommitted() )
log.debug("ACCCK! Response Already Committed");
}
However, I noticed that my x-success header was never added. A little digging, and I noticed that my response was already returned/committed by the time the control returned to my filter chain.
What is the proper way to do this in Struts1? Does the filter execution not supposed to wrap the entire servlet? Why is my response being committed prior to my filter finishing? Where else can I add headers based on the response (post action processing) in Struts1?
When you call filterChain.doFilter you pass control from your filter to requested page (ie Struts), which is then free to commit the response if it chooses. Since you want to examine the result of the Struts servlet, you should create a response wrapper class (extends HttpServletRequestWrapper) and pass that to filterChain.doFilter rather than the response parameter that your filter is passed.
HttpServletResponse httpResponse = (HttpServletResponse) servletResponse;
ServletResponse wrapper = new MyHttpServletRequestWrapper(httpResponse);
filter.doChain(servletRequest, wrapper);
The HttpServletRequestWrapper constructor accepts a HttpServletResponse as input and delegates all methods to the wrapped response, but you can override them in your class as necessary. If you want to prevent the response from being committed you'll want to override methods such as flushBuffer, getOutputSteam (the returned output stream can be flushed, committing the response), and getWriter.
But you may not need to do that - try overriding the setStatus method in the wrapper class to check the status code and add the header when it is called:
public class MyHttpServletResponseWrapper extends HttpServletResponseWrapper {
public MyHttpServletResponseWrapper(HttpServletResponse response) {
super(response);
}
#Override
public void setStatus(int sc) {
if(sc == 200) {
addIntHeader("x-success", 1);
}
super.setStatus(sc);
}
}
Although this question was asked in relation to Struts 1, it applies to any web framework; my answer is based on work I did for JSF filters.
I have created a camel-jetty http proxy bridge for my project's restful services for document download/upload. These services are invoked to get/upload documents of different types, most of the times the file size is more than 100MB.
When I invoke the upload rest service(HTTP POST) directly(not routing it via camel-jetty http proxy) to upload the document of 100MB it only takes around 2-3 mins to complete the upload and receive the response back however when I route the request via camel route it takes more than 15 mins which is kind of weird as the camel route in play is nothing but a HTTP proxy.
Following are some info:
Camel Version: 2.15.1
Camel Route definition:
<route>
<from uri="jetty:http://0.0.0.0:8383/sqidds/document?disableStreamCache=true&matchOnUriPrefix=true&enableMultipartFilter=false&continuationTimeout=-1" />
<log id="incomingMessage" message="incomingMessage - \n[id = ${id}]\n [headers = ${headers}]" />
<to uri="jetty:http://somehost:8080/sqidds/document?bridgeEndpoint=true&throwExceptionOnFailure=false&httpClient.timeout=3200000" />
<log id="outgoingMessage" message="outgoingMessage - \n[id = ${id}]\n [headers = ${headers}]" />
</route>
Camel Project POM Excerpt:
.
.
.
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-core</artifactId>
<version>2.15.1.redhat-621084</version>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-spring</artifactId>
<version>2.15.1.redhat-621084</version>
</dependency>
.
.
.
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-jetty</artifactId>
<version>2.15.1.redhat-621084</version>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-http</artifactId>
<version>2.15.1.redhat-621084</version>
<!-- use the same version as your Camel core version -->
</dependency>
.
.
.
Rest Service(Spring MVC) Code:
#RequestMapping(method = RequestMethod.POST, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public #ResponseBody String saveFile(#RequestPart("file") MultipartFile file) throws IOException, DocumentNotSavedException {
String targetPath = null;
if (file != null) {
String repoDirectoryPath = "SOME_REPO_PATH";
String uniqueFileName = FileUtil.getUniqueFileName(repoDirectoryPath, file.getOriginalFilename());
File targetFile = new File(repoDirectoryPath, uniqueFileName);
targetPath = targetFile.getCanonicalPath();
FileUtils.copyInputStreamToFile(file.getInputStream(), targetFile);
} else {
log.error("File is null");
throw new DocumentNotSavedException("File data could not be saved");
}
return targetPath;
}
RestClient Code:
public String putDocument(File file,String fileName) throws RestClientException{
ResponseEntity<String> response = null;
try {
File file = new File("SOME_100MB_FILE.pdf");
byte[] fileBytes = new byte[(int)file.length()];
LinkedMultiValueMap<String, Object> map = new LinkedMultiValueMap();
ByteArrayResource contentsAsResource = new ByteArrayResource(fileBytes, fileName) {
#Override
public String getFilename() {
return this.getDescription();
}
};
map.add("file", contentsAsResource);
httpHeaders.setContentType(MediaType.MULTIPART_FORM_DATA);
HttpEntity<LinkedMultiValueMap<String, Object>> requestEntity = new HttpEntity<LinkedMultiValueMap<String, Object>>(map, httpHeaders);
response = restTemplate.exchange(serverUri, HttpMethod.POST, requestEntity, String.class);
} catch (Exception e) {
logger.error("Exception in put document service", e);
throw new RestClientException("Exception in put document service :",e);
}
return response.getBody();
}
NOTE: For a 100MB file, the camel route log for incoming message is logged within a sec after the service is invoked, however I see the outgoing log after around 15 mins. I think there might be something wrong with the producer camel route.
There is an improvement in the camel 2.17.0 which can use response input stream directly in http producer.
Can you try out the latest Camel 2.17.x release (2.17.3) with your route?
This is in reference to the following thread
[File upload doesn't work with AJAX in PrimeFaces 4.0/JSF 2.2.x - javax.servlet.ServletException: The request content-type is not a multipart/form-data
The problem I experience is Nullpointer on clicking the command button.
Starting from the web.xml
<context-param>
<param-name>primefaces.UPLOADER</param-name>
<param-value>commons</param-value>
</context-param>
xhtml
<p:fileUpload id="file" value="#{userBean.uploadedFile}"
mode="simple" required="true" allowTypes="*.xls,*.xlsx"
requiredMessage="#{msg.vrUserUpload}"
invalidFileMessage="#{msg.vrUserUploadInvalidFile}"
multiple="false" fileUploadListener="userBean.fileUploadListener" />
<p:commandButton id="btnUpload" value="#{displayText.btUpload}"
styleClass="button_lite" actionListener="#{userBean.insert}"
ajax="true" update="userMassUploadForm"
process="userMassUploadForm">
</p:commandButton>
UserBean.java
public void fileUploadListener(FileUploadEvent event)
{
uploadedFile = event.getFile();
}
public void insert(){
if(uploadedFile!=null){
System.out.println(uploadedFile.getFileName());
}
else{
System.out.println("The file object is null.");
}
}
Console prints out "The file object is null." whenever ajax="true" and when set to false, works. I could not find a solution for this in the above referred thread.
Please advise.Also please let me know if you want any further information.
From PrimeFaces user guide:
Simple File Upload
Simple file upload mode works in legacy mode with a file input whose value should be an UploadedFile instance. Ajax uploads are not supported in simple upload.
Hi i have a command button.
I am using jsf 1.12 and tomahwak. I also am using jquery on client side.
<h:commandButton type="submit" value="Download Receipt"
onclick="refresh();"
id="downloadDocument"
actionListener="#{transactionPage.downloadReceipt}"
immediate="true"
/>
My jsf backing bean function
public void downloadReciept(final ActionEvent event) {
try{
DocManager docManager = new DocManager();
docManager.printDocs();
}catch (Exception e) {
log.error("Fail to download document!", e);
}
}
print docs would just create a file and stream it by setting the content-type, response, etc.
File sourceFile = createDoc();
Url url = sourceFile.toURI().toURL();
streamDoc(url);
I want to be able to display a message when downloading is starting and message when it finished
Hi thanks found my solution.
I change to commandLink than
set the cookie in backend and use jquery's file download
After upgrading to GlassFish 4 and JSF 2.2 Primefaces FileUploadEvent stop working. With JSF 2.1 it was working with no problem. Everything is working fine except file uploading. Is there something that I am missing?
GlassFish 4
JSF 2.2
PrimeFaces 3.4.2 and 3.5
Commons io version: 2.4
Commons fileupload version: 1.3
Controller side
public void handleFileUpload(FileUploadEvent event) {
System.out.println("HandleFileUpload");
byte[] file = event.getFile().getContents();
newFieldset.setData(file);
FacesMessage msg = new FacesMessage("Succesful", event.getFile().getFileName() + " is uploaded.");
FacesContext.getCurrentInstance().addMessage(null, msg);
}
View
<h:form enctype="multipart/form-data">
<p:fieldset legend="Create new feed" toggleable="true" collapsed="true" >
<p:fileUpload fileUploadListener="#{adminHomeController.handleFileUpload}" style="margin-top: 20px;"
mode="advanced"
update="messages"
sizeLimit="1000000"
multiple="false"
allowTypes="/(\.|\/)(gif|jpe?g|png)$/"/>
<p:inputText label="Baslik" style="margin-top: 20px;" required="true" value="#{adminHomeController.newFieldset.legend}" />
<p:editor style="margin-top: 20px;"
value="#{adminHomeController.newFieldset.content}" />
<p:commandButton style="margin-top: 20px;" value="#{msg['common.save']}" update="messages" icon="ui-icon-disk" actionListener="#{adminHomeController.saveFieldset()}"/>
</p:fieldset>
<p:growl id="messages" showDetail="true"/>
</h:form>
I was finally able to figure it out. Commons-fileuploads method parseRequest(httpServletRequest) tries to read the request's inputStream. Since the container already read it, it is empty. So what can be done to solve this? The answer is a bit more complicated than I initially thought it would be. First you will need your own FileUploadFilter which could look like this:
public class FileUploadFilter implements Filter
{
private final static Logger LOGGER = LoggerFactory.getLogger(FileUploadFilter.class);
/*
* (non-Javadoc)
*
* #see javax.servlet.Filter#init(javax.servlet.FilterConfig)
*/
#Override
public void init(FilterConfig filterConfig) throws ServletException
{
}
/*
* (non-Javadoc)
*
* #see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest,
* javax.servlet.ServletResponse, javax.servlet.FilterChain)
*/
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException
{
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
boolean isMultipart = (httpServletRequest.getContentType() == null) ? false : httpServletRequest.getContentType().toLowerCase().startsWith("multipart/");
if (isMultipart)
{
MultipartRequest multipartRequest = new MultipartRequest(httpServletRequest);
LOGGER.info("File upload request parsed succesfully, continuing with filter chain with a wrapped multipart request");
filterChain.doFilter(multipartRequest, response);
}
else
{
filterChain.doFilter(request, response);
}
}
/*
* (non-Javadoc)
*
* #see javax.servlet.Filter#destroy()
*/
#Override
public void destroy()
{
LOGGER.info("Destroying UploadFilter");
}
Next: Register this filter in your web.xml and remove/replace the Primefaces filter. This should look something like this:
<filter>
<filter-name>FileUpload Filter</filter-name>
<filter-class><YourPackage>.FileUploadFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>FileUpload Filter</filter-name>
<servlet-name>Faces Servlet</servlet-name>
</filter-mapping>
Unfortunately thats not it. You will need your own MultipartRequest since you have to assemble the list of FileItems by yourself. But Stop. We have to work with the javax.servlet.Part classes which are not compatible with the FileItem. So i wrote a new class which bridges these two. You can find this class here: http://pastebin.com/JcfAYjey
The last piece of the puzzle is the mentioned MultipartRequest which links the PartItem and the FileUploadFilter. I took this class from the Primefaces-Repository and changed it according to out needs (see http://pastebin.com/Vc5h2rmJ). The difference is between lines 47 and 57.
So what do you have to do:
1. Create the three classes FileUploadFilter, MultipartRequest and PartItem
2. Register the FileUploadFilter in your web.xml
3. Enjoy!
PLEASE NOTE: This is not intended as a solve-all-problems solution but a merely a direction you may take in further implementations. The MultipartRequest for example will only work for parts with content-type image/*. You may need to change this.
Feel free to change the code ;) Hope it helps!
EDIT: I forgot to mention one important step. You will additionally need your Own FileIUploadRenderer. The one Primefaces implemented uses an instanceof check to find the MultipartRequest. Since you are now using a different one the import has to be changed. The rest of the class can stay the same (http://pastebin.com/rDUkPqf6). Don't forget to register it inside of your faces-config.xml :
<render-kit>
<renderer>
<component-family>org.primefaces.component</component-family>
<renderer-type>org.primefaces.component.FileUploadRenderer</renderer-type>
<renderer-class><YourPackage>.FileUploadRenderer</renderer-class>
</renderer>
</render-kit>
Answer lies in UploadFile getInputstream() method. Don't rely on getContents() method.
This is my simple solution which worked with the below dependencies in glassfish 4
Primefaces 4.0.RC1
jsf 2.2
commons-fileupload 1.3
private byte[] getFileContents(InputStream in) {
byte[] bytes = null;
try {
// write the inputStream to a FileOutputStream
ByteArrayOutputStream bos = new ByteArrayOutputStream();
int read = 0;
bytes = new byte[1024];
while ((read = in.read(bytes)) != -1) {
bos.write(bytes, 0, read);
}
bytes = bos.toByteArray();
in.close();
in = null;
bos.flush();
bos.close();
bos = null;
logger.debug("New file created!");
} catch (IOException e) {
System.out.println(e.getMessage());
}
return bytes;
}
getFileContents(getFile().getInputstream());
Try to delete beans.xml (CDI configuration file) and use JSF beans.
I saw on PrimeFaces blog that full JSF 2.2 support will be as of version 4.0.
See 3.5 is missing dependency - so won't launch
I think it's a commons-fileupload issue. When I debug through the code, the PrimeFaces' UploadFilter triggers correctly the commons-fileupload's FileUploadBase.parseRequest method (identically flow when I use GlassFish 3.1.22 or GlassFish 4), but the check on FileItemIterator.hasNext returns false.