In struts2, file is downloaded as stream using StreamResult class.
To download file of dynamic name, size or type we need to extend this StreamResult class.
StreamResult result type takes the following parameters:
* contentType - the stream mime-type as sent to the web browser (default = text/plain).
* contentLength - the stream length in bytes (the browser displays a progress bar).
* contentDispostion - the content disposition header value for specifing the file name (default = inline, values are typically filename="document.pdf".
* inputName - the name of the InputStream property from the chained action (default = inputStream).
* bufferSize - the size of the buffer to copy from input to output (default = 1024).
Example:
<result name="success" type="stream">
<param name="contentType">image/jpeg</param>
<param name="inputName">imageStream</param>
<param name="contentDisposition">filename="document.pdf"</param>
<param name="bufferSize">1024</param>
</result>
So in the extended StreamResult class we need to set these parameters dynamically.
You could download source code from here, its same as following:
package downloadexample;
import org.apache.struts2.dispatcher.StreamResult;
import com.opensymphony.xwork2.ActionInvocation;
/**
* This class for result-type="myStream"
*
* <result-types> <result-type name="myStream" default="false"
* class="downloadexample.DynamicStreamResult"/>
*
* </result-types>
*
* It extends StreamResult Used to download file as a stream.
*
* @author sheetal
*
*/
public class DynamicStreamResult extends StreamResult{
@Override
protected void doExecute(String finalLocation,
ActionInvocation invocation)
throws Exception {
//get name of downloaded file
String downloadedFileName = invocation.getStack().
findValue(conditionalParse
("name", invocation));
contentDisposition = "filename=\""
+downloadedFileName + "\"";
//get file size
contentLength = ""+ invocation.getStack().findValue(
conditionalParse("size", invocation));
// get type of file
contentType = ""+ invocation.getStack().
findValue(
conditionalParse("description", invocation));
/*
Executes the result given a final location
(jsp page, action, etc) and
the action invocation (the state in which
the action was executed).
*/
super.doExecute(finalLocation, invocation);
}
}
Let, our site is a search site where user inputs name of a file. our system searches the file in server's local directory and lets the user download it if found.
To do this, .jsp file should be include the following:
<s:form action="downloadFile" validate="true">
<s:textfield label="Search file"
name="name" required="true"/>
<s:textfield label="Define file type (image/jpeg, text/plain, application/pdf)"
name="description" required="true"/>
<s:submit value="Find file"/>
</s:form>
where downloadFile is the action for downloading file.
In your action class, add the following lines:
private String name;
//holds name of downloaded file
private InputStream inputStream;
//holds stream of downloaded file
private String description;
//holds the content type of the downloaded file
private long size;
//holds the content size of the downloaded file
//method for downloading file
public String downloadFile()
{
/*
let, method searchFile(String fileName)
does the searching for us
& returns InputStream of the file if found
and null otherwise.
*/
this.inputStream = searchFile(name);
if(inputStream !=null)
{
return Action.SUCCESS;
}
else
{
//handle error
return Action.ERROR;
}
}
//write setter getter methods
public InputStream getInputStream() throws Exception
{
return inputStream;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public String getDescription()
{
return this.description;
}
public void setDescription(String description)
{
this.description = description;
}
// write getter setter for attribute size
Now, edit your struts.xml file:
<!-- custom result type for file download -->
<result-types>
<result-type name="myStream"
default="false"
class="downloadexample.DynamicStreamResult"/>
</result-types>
<!-- action for downloading file-->
<action name="downloadFile"
method="downloadFile"
class="<action-class-name>">
<result type="myStream"/>
<result name="error">jsps/your_error_page.jsp</result>
</action>
syntax highlighted by Code2HTML, v. 0.9.1
& we are done :D.
...................................................
May be a Better method:
the time i wrote this post i was too naive to find other solution for this. One person commented a quick solution on the blog.... thank you again. i haven't tried it, so i'm not sure if it works, check yourself...here is the comment.........
Actually, there's no need to extend the StreamResult class. You can dynamically pass the contentType (and other Stream parameters) by using parameter substitution in your Action mapping, like so:
<result name="success" type="stream">
<param name="contentType">${contentType}</param>
<param name="inputName">imageStream</param>
<param name="contentDisposition">filename="${fileName}"</param>
<param name="bufferSize">${bufferSize}</param>
</result>
You then add methods set/getContentType(), set/getFileName() and set/getBufferSize() to your Action class. In the Action method which handles your business logic, all you have to do is call this.setContentType(), this.setFileName() and this.setBufferSize() and supply whatever values you like.
---------------------------------------