Download file in Struts 2 using annotation - struts2

I would like to ask if anyone can help me with an example to download a file using Struts 2 annotation
I have tried this in my action class
public class MyClass {
private InputStream fileInputStream;
private String fileName;
#Override
#Action(AbstractBasisAction.VIEW)
public String view() {
System.out.println("HoursCycleDownloadFrameAction: view");
super.view();
return SUCCESS;
}
public InputStream getFileInputStream() {
return fileInputStream;
}
#Action(value = "downloadFile", results = { #Result(name = "success", type = "stream", params = { "contentType", "application/octet-stream", "inputName", "fileInputStream", "contentDisposition", "filename=\"${fileName}\"", "bufferSize", "1024" }) })
public String downloadFile() throws Exception {
fileName = "license.txt";
fileInputStream = new FileInputStream(new File("C:\\", fileName));
return SUCCESS;
}
}
and this is what my page contains
<s:url id="fileInputStream" namespace="/myClass" action="downloadFile" ></s:url>
Download file - <s:a href="%{fileInputStream}">lisence.txt</s:a>
but the problem now is that it downloads the file with the action method name. Means that the file name is downloadFile.action. Can anyone help me with that?

1) if it is a .txt file, "contentType", "application/octet-stream"
should instead be "contentType", "plain/text";
2) you need a getter for your private String fileName; variable;
3) "contentDisposition", "filename=\"${fileName}\"" should contains extension, and eventually inline (default, open in browser) or attachment (ask if donwload or open with client application), for example
"contentDisposition", "attachment; filename=\"${fileName}.txt\""

Related

How to generate interchangeable download links?

i'm tying to make DL link so others couldn't dl the same file by sharing it
so far i've found this code
public FileResult Download()
{
byte[] fileBytes = System.IO.File.ReadAllBytes(#"c:\folder\myfile.ext");
string fileName = "myfile.ext";
return File(fileBytes, System.Net.Mime.MediaTypeNames.Application.Octet, fileName);
}
it won't make interchangeable links ,how can we do that?
Try this Example:
public ActionResult Download()
{
var filePath=#"c:\folder\myfile.ext";
var fileBytes = System.IO.File.ReadAllBytes(filePath);
var response = new FileContentResult(fileBytes, "application/octet-stream")
{
FileDownloadName = Path.GetFileName(filePath)
};
return response;
}

Accessing resources in Grails Plugin

I have a Grails Plugin called 'foo' that uses another Grails Plugin called 'common'.
grails.plugin.location.'common' = "../common"
The 'common' plugin contains domain classes, as well as resource files (.properties files, xml templates, ...). These files are all located in subfolders in common/grails-app/conf/.
There's one class that implements NamespaceContext in my 'common' plugin that uses these files in order to function properly.
public class MyNamespaceContext implements NamespaceContext {
private Map<String, String> namespaces;
public MyNamespaceContext() {
final String XML_NAMESPACES_FILE = "grails-app/conf/xml/xmlNamespaces.properties";
try {
Properties xmlNamespaces = new Properties();
xmlNamespaces.load(new FileReader(XML_NAMESPACES_FILE));
namespaces = new HashMap<String, String>((Map) xmlNamespaces);
} catch (FileNotFoundException e) {
throw new RuntimeException("XML namespaces file '" + XML_NAMESPACES_FILE + "' cannot be found");
} catch (IOException e) {
throw new RuntimeException("IOException");
}
}
...
}
This class is used in several classes, also located in 'common' that form my domain model, implemented as xml decorators.
public class UserXmlDecorator implements User {
private Document xmlDocument;
private XPath xPath;
private final String rawXml;
public UserXmlDecorator(String rawXml) {
this.rawXml = rawXml;
this.xmlDocument = XmlDocumentFactory.INSTANCE.buildXmlDocumentInUTF8(rawXml);
this.xPath = XPathFactory.newInstance().newXPath();
xPath.setNamespaceContext(new MyNamespaceContext());
}
public String getUserName() {
try {
XPathExpression userNameXPathExpr = xPath.compile("...");
String userName = userNameXPathExpr.evaluate(appendixBXmlDocument);
return userName;
} catch (XPathExpressionException e) {
throw new RuntimeException();
}
}
public String getAge() {
try {
XPathExpression ageXPathExpr = xPath.compile("...");
String age = ageXPathExpr.evaluate(appendixBXmlDocument);
return age;
} catch (XPathExpressionException e) {
throw new RuntimeException();
}
}
When creating these decorators in my Grails Plugin 'foo', I get a FileNotFound exception, because it is looking for the template in foo/grails-app/conf/xml/xmlNamespaces.properties, instead of common/grails-app/conf/xml/xmlNamespaces.properties.
I've read
Grails: How to reference a resource located inside an installed plugin? but this could not help me.
Any idea how I can solve this?
Solved this by putting the .properties file in the classpath instead of the conf/ directory and then using the classloader to lod the resource.
xmlNamespaces.load(this.getClass().getClassLoader().getResourceAsStream(XML_NAMESPACES_FILE));

Using memorystream and DotNetZip in MVC gives "Cannot access a closed Stream"

I'm trying to create a zipfile in a MVC method using the DotNetZip components.
Here is my code:
public FileResult DownloadImagefilesAsZip()
{
using (var memoryStream = new MemoryStream())
{
using (var zip = new ZipFile())
{
zip.AddDirectory(Server.MapPath("/Images/"));
zip.Save(memoryStream);
return File(memoryStream, "gzip", "images.zip");
}
}
}
When I run it I get a "Cannot access a closed Stream" error, and I'm not sure why.
Don't dispose the MemoryStream, the FileStreamResult will take care once it has finished writing it to the response:
public ActionResult DownloadImagefilesAsZip()
{
var memoryStream = new MemoryStream();
using (var zip = new ZipFile())
{
zip.AddDirectory(Server.MapPath("~/Images"));
zip.Save(memoryStream);
return File(memoryStream, "application/gzip", "images.zip");
}
}
By the way I would recommend you writing a custom action result to handle this instead of writing plumbing code inside your controller action. Not only that you will get a reusable action result but bear in mind that your code is hugely inefficient => you are performing the ZIP operation inside the memory and thus loading the whole ~/images directory content + the zip file in memory. If you have many users and lots of files inside this directory you will very quickly run out of memory.
A much more efficient solution is to write directly to the response stream:
public class ZipResult : ActionResult
{
public string Path { get; private set; }
public string Filename { get; private set; }
public ZipResult(string path, string filename)
{
Path = path;
Filename = filename;
}
public override void ExecuteResult(ControllerContext context)
{
if (context == null)
{
throw new ArgumentNullException("context");
}
var response = context.HttpContext.Response;
response.ContentType = "application/gzip";
using (var zip = new ZipFile())
{
zip.AddDirectory(Path);
zip.Save(response.OutputStream);
var cd = new ContentDisposition
{
FileName = Filename,
Inline = false
};
response.Headers.Add("Content-Disposition", cd.ToString());
}
}
}
and then:
public ActionResult DownloadImagefilesAsZip()
{
return new ZipResult(Server.MapPath("~/Images"), "images.zip");
}
Couldn't comment.
Darin's answer is great! Still received a memory exception though so had to add response.BufferOutput = false; and because of that had to move content-disposition code higher.
So you have:
...
var response = context.HttpContext.Response;
response.ContentType = "application/zip";
response.BufferOutput = false;
var cd = new ContentDisposition
{
FileName = ZipFilename,
Inline = false
};
response.Headers.Add("Content-Disposition", cd.ToString());
using (var zip = new ZipFile())
{
...
Just in case it wasn't obvious :)

Saving HTML report by showing Save As dialog

I want to show a Save As dialog box to user in my MVC application and allow him to save some HTML report in the format of pdf or word. For doing this, do I need to play with File stream and IO functions at server side? Or is it possible at JQuery level itself?
I found some references on web like adding a response header Content-Disposition, but not getting how to apply it. Can you please suggest some options?
You must create a descendant from ActionResult that plays with output the desired way.
This is a class of mine I created to implement a "Save as Excel" feature:
public class ExcelResult : ActionResult
{
private string _fileName;
private IQueryable _rows;
private string[] _headers = null;
private string _data;
private TableStyle _tableStyle;
private TableItemStyle _headerStyle;
private TableItemStyle _itemStyle;
public string FileName
{
get { return _fileName; }
}
public IQueryable Rows
{
get { return _rows; }
}
public ExcelResult(string data, string fileName)
{
_fileName = fileName;
_data = data;
}
public override void ExecuteResult(ControllerContext context)
{
WriteFile(_fileName, "application/ms-excel", _data);
}
private string ReplaceSpecialCharacters(string value)
{
value = value.Replace("’", "'");
value = value.Replace("“", "\"");
value = value.Replace("”", "\"");
value = value.Replace("–", "-");
value = value.Replace("…", "...");
return value;
}
private void WriteFile(string fileName, string contentType, string content)
{
HttpContext context = HttpContext.Current;
context.Response.Clear();
context.Response.AddHeader("content-disposition", "attachment;filename=" + fileName);
context.Response.Charset = "";
context.Response.Cache.SetCacheability(HttpCacheability.NoCache);
context.Response.ContentType = contentType;
context.Response.Write(content);
context.Response.End();
}
}
You can use this example to generate HTML for word. PDF are a different matter, tho'.

FileContentResult and international characters

I am using a fileContentResult to render a file to the browser. It works well except that it throws an exception when the fileName contains international characters.
I remember reading somewhere that this feature does not support international characters but I am sure there mustbe a workaround or a best practice people follow in cases the application needs to upload files in countries other than US.
Does anyone know of such a practice?Here is the ActionResult Method
public ActionResult GetFile(byte[] value, string fileName)
{
string fileExtension = Path.GetExtension(fileName);
string contentType = GetContentType(fileExtension); //gets the content Type
return File(value, contentType, fileName);
}
THanks in advance
Susan
public class UnicodeFileContentResult : ActionResult {
public UnicodeFileContentResult(byte[] fileContents, string contentType) {
if (fileContents == null || string.IsNullOrEmpty(contentType)) {
throw new ArgumentNullException();
}
FileContents = fileContents;
ContentType = contentType;
}
public override void ExecuteResult(ControllerContext context) {
var encoding = UnicodeEncoding.UTF8;
var request = context.HttpContext.Request;
var response = context.HttpContext.Response;
response.Clear();
response.AddHeader("Content-Disposition", string.Format("attachment; filename={0}", (request.Browser.Browser == "IE") ? HttpUtility.UrlEncode(FileDownloadName, encoding) : FileDownloadName));
response.ContentType = ContentType;
response.Charset = encoding.WebName;
response.HeaderEncoding = encoding;
response.ContentEncoding = encoding;
response.BinaryWrite(FileContents);
response.End();
}
public byte[] FileContents { get; private set; }
public string ContentType { get; private set; }
public string FileDownloadName { get; set; }
}
I don't think it's possible to download files with international characters in the file name. The file name is part of the Content-disposition header, and like all HTTP headers, there's no way of using a different encoding other than ASCII that will work across all browsers and proxies.
Uploading files with international characters should be no problem, though, since the file name is transmitted as normal form data (application/www-url-encoded)
I think it depends on your responseHeaderEncoding (see http://msdn.microsoft.com/en-us/library/hy4kkhe0.aspx )
HTH,
Erik
public FileContentResult XmlInvoice(Order order)
{
string stream = order.Win1250StringData;
var bytes = Encoding.GetEncoding("windows-1250").GetBytes(stream);
var fr = new FileContentResult(bytes, "application/xml");
fr.FileDownloadName = string.Format("FV{0}.xml", order.DocumentNumber);
return fr;
}
The bytes gets from UTF-8 or Win1250 have different size. You must interpret string right way by getting bytes from string in right Encoding.

Resources