FileContentResult and international characters - asp.net-mvc

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.

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;
}

How to return json date from MVC4 controller in ISO format

I tried tro return date in ISO format using Json.Net from ASP.NET MVC4 controller
public JsonResult Sales() {
var saleList = new List<Sale>();
...
var str = JsonConvert.SerializeObject(saleList);
return Json(str, JsonRequestBehavior.AllowGet);
}
public class Sale
{
public DateTime saledate { get; set; }
...
}
But it returns whole object json notation as single string.
How to return date in ISO format as json object ?
You can do it with ServiceStack JSON serializer but first you have to integrate it to ASP.NET MVC.
After installing the package, configure DateTime serialization in application start:
JsConfig.DateHandler = JsonDateHandler.ISO8601;
Create an ActionResult type for JSON content:
public class CustomJsonResult : ActionResult
{
private readonly object _data;
private readonly string _content;
private readonly Encoding _encoding;
public CustomJsonResult(object data) : this(data, null, null) { }
public CustomJsonResult(object data, string content) : this(data, content, null) { }
public CustomJsonResult(object data, Encoding encoding) : this(data, null, encoding) { }
public CustomJsonResult(object data, string content, Encoding encoding)
{
_data = data;
_content = content;
_encoding = encoding;
}
public override void ExecuteResult(ControllerContext context)
{
if (context == null)
{
throw new ArgumentNullException("context");
}
HttpResponseBase response = context.HttpContext.Response;
response.ContentType = string.IsNullOrEmpty(_content) ? "application/json" : _content;
if (_encoding != null)
{
response.ContentEncoding = _encoding;
}
response.Write(JsonSerializer.SerializeToString(_data));
}
}
Then you can add these methods to a base controller:
protected CustomJsonResult CustomJson(object data)
{
return new CustomJsonResult(data);
}
protected CustomJsonResult CustomJson(object data, string content)
{
return new CustomJsonResult(data, content);
}
protected CustomJsonResult CustomJson(object data, Encoding encoding)
{
return new CustomJsonResult(data, encoding);
}
protected CustomJsonResult CustomJson(object data, string content, Encoding encoding)
{
return new CustomJsonResult(data, content, encoding);
}
At last you can return the result like this:
return CustomJson(saleList);
You can set settings when using an overload to SerializeObject that takes an JsonSerializerSettings parameter:
public static string SerializeObject(
Object value,
JsonSerializerSettings settings
)
The JsonSerializerSettings have a property called DateFormatHandlingused to distinguish between Microsoft Format and ISO format.
You could also use a custom converter in JSON.NET. Custom Converters can be applied using the CustomConverter attribute.
An example can be found in the JSON.NET documentation: http://james.newtonking.com/json/help/index.html
I would prefer the first possibility.

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'.

MVC 3 Cant get streamed images to show in Internet explorer or Chrome

I'm having a bit of a issue with getting my streamed images to show in Internet Explorer or Google Chrome but they appear fine in FireFox. I've pasted my code below, I've put it together using a load of bits and bobs I've found googleing.
public ImageResult GetPhotoS(string photoID, int userID, int? galleryID)
{
if (galleryID == null)
{
string thumbLocation = string.Format("{0}{1}\\Pics\\{2}_thumb.jpg", ConfigurationManager.AppSettings["PhotoLocation"].ToString(), Convert.ToInt32(User.Identity.Name), photoID);
using (FileStream stream = new FileStream(thumbLocation, FileMode.Open))
{
FileStreamResult fsResult = new FileStreamResult(stream, "image/jpeg");
ImageResult result = new ImageResult(ReadFully(fsResult.FileStream), "image/jpeg");
return result;
}
}
}
private static byte[] ReadFully(Stream input)
{
byte[] buffer = new byte[16 * 1024];
using (MemoryStream ms = new MemoryStream())
{
int read;
while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
{
ms.Write(buffer, 0, read);
}
return ms.ToArray();
}
}
public class ImageResult : ActionResult
{
public String ContentType { get; set; }
public byte[] ImageBytes { get; set; }
public String SourceFilename { get; set; }
//This is used for times where you have a physical location
public ImageResult(String sourceFilename, String contentType)
{
SourceFilename = sourceFilename;
ContentType = contentType;
}
//This is used for when you have the actual image in byte form
// which is more important for this post.
public ImageResult(byte[] sourceStream, String contentType)
{
ImageBytes = sourceStream;
ContentType = contentType;
}
public override void ExecuteResult(ControllerContext context)
{
var response = context.HttpContext.Response;
response.Clear();
response.Cache.SetCacheability(HttpCacheability.NoCache);
response.ContentType = ContentType;
//Check to see if this is done from bytes or physical location
// If you're really paranoid you could set a true/false flag in
// the constructor.
if (ImageBytes != null)
{
var stream = new MemoryStream(ImageBytes);
stream.WriteTo(response.OutputStream);
stream.Dispose();
}
else
{
response.TransmitFile(SourceFilename);
}
}
}
I'm displaying the images using the following:
<img src="#Url.Action("GetPhotoS", "Image", new { photoID = photo.ID, userID = Convert.ToInt32(User.Identity.Name) })" alt="#photo.Description" />
All I get from Chrome and IE are the usual red crosses where the image should be. Any help would be appreciated.
Have you tried returning a FileContentResult?
public FileContentResult GetPhotoS(string photoID, int userID, int? galleryID)
{
if (galleryID == null)
{
string thumbLocation = string.Format("{0}{1}\\Pics\\{2}_thumb.jpg", ConfigurationManager.AppSettings["PhotoLocation"].ToString(), Convert.ToInt32(User.Identity.Name), photoID);
using (FileStream stream = new FileStream(thumbLocation, FileMode.Open))
{
return File(ReadFully(stream), "image/jpeg");
}
}
throw new FileNotFoundException("Could not find gallery");
}
This also seems a bit redundant, why not just concatenate a URL using the photoId, userId, and galleryId? Are the images stored outside of the webroot?

Resources