views:

267

answers:

3

Hello, I'm trying to serve a txt file made from the database using an action. The action is the following:

public ActionResult ATxt()
{
    var articulos = _articulosService.ObteTotsArticles();
    return File(CatalegATxt.ATxt(articulos), "text/plain");
}

and the CatalegATxt class is:

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using WebDibaelsaMVC.DTOs.Busqueda;

namespace WebDibaelsaMVC.TxtLib
{
    public static class CatalegATxt
    {
         public static Stream ATxt(IEnumerable<ArticuloBusquedaDTO> articles)
         {
            var stream = new MemoryStream();
            var streamWriter = new StreamWriter(stream, Encoding.UTF8);
            foreach (ArticuloBusquedaDTO article in articles)
            {
                streamWriter.WriteLine(article.ToStringFix());
            }
            stream.Seek(0, SeekOrigin.Begin);
            return stream;
        }

        public static string ToStringFix(this ArticuloBusquedaDTO article)
        {
            string result = "";
            result += article.CodigoArticulo.PadRight(10, ' ').Substring(0, 10);
            result += article.EAN.Trim().PadLeft(13, '0').Substring(0, 13);
            result += article.NombreArticulo.PadRight(100, ' ').Substring(0, 100);
            result += article.Marca.PadRight(100, ' ').Substring(0, 100);
            result += article.Familia.PadRight(50, ' ').Substring(0, 50);
            result += article.PrecioCesion.ToStringFix();
            result += article.PVP.ToStringFix();
            return result;
        }

        private static string ToStringFix(this double numero)
        {
            var num = (int)Math.Round(numero * 100, 0);
            string result = num.ToString().PadLeft(10, '0');
            return result;
        }
    }
}

it just writes the file lines based on the stuff I got from the database. But when I look at the file it looks truncated. The file is about 8Mb. I also tried converting to byte[] before returning from ATxt with the same result.

Any idea?

Thanks,

Carles

Update: I also tried to serve XML from the same content and it also gets truncated. It doesn't get truncated on the data (I thought it might have been an EOF character in it) but it truncates in the middle of a label...

A: 

You probably want to close the MemoryStream. It could be getting truncated because it expects more data still. Or to make things even simpler, try something like this:

public static byte[] ATxt(IEnumerable<ArticuloBusquedaDTO> articles)
{
   using(var stream = new MemoryStream())
   {
        var streamWriter = new StreamWriter(stream, Encoding.UTF8);
        foreach (ArticuloBusquedaDTO article in articles)
        {
            streamWriter.WriteLine(article.ToStringFix());
        }

        return stream.ToArray();
    }
}
Jedidja
I already tried to do this with the same result. Also, closing the stream before returning it results in an error (the view tries to acces a closed stream...)
Carles
+3  A: 

Why are you using an ActionResult?

ASP.NET MVC 1 has a FileStreamResult for just what you are doing. It expects a Stream object, and returns it.

public FileStreamResult Test()
{
  return new FileStreamResult(myMemoryStream, "text/plain");
}

Should work fine for what you want to do. No need to do any conversions.

In your case, just change your method to this:

public FileStreamResult ATxt()
{
    var articulos = _articulosService.ObteTotsArticles();
    return new FileStreamResult(CatalegATxt.ATxt(articulos), "text/plain");
}
thorkia
I thought that `FileStreamResult` extends `ActionResult` and it is the same. Also the function `File` returns a `FileStreamResult`...
Carles
FileStreamResult extends FileResult. It is specifically designed to send a stream to the client. I have used it regularly to send excel files to users that are in the neighbourhood of 10-15 megs and never have them truncated.
thorkia
FileResult does extend ActionResult, but they handle output differently.If you want to send a file that is stored on the hard drive, you should use FileResultIf you want to send the contents of a file, you should use FileStreamResult
thorkia
I'm still thinking your method and mine are equivalent...
Carles
If you don't want a perfectly good answer, don't ask the question?
TFD
This answer does not answer my question... If you look at my method you'll see I'm returning a FileStreamResult. What if depending on the parameters I want to return a different type of ActionResult? Does it not work then? For the record, I tried what this answer sugests and I get the same result...
Carles
I'm starting to think that my question might not be correct and the problem is elsewhere.
Carles
+1  A: 

I was having the exact same problem. The text file would always be returned as truncated.

It crossed my mind that it might be a "flushing" problem, and indeed it was. The writer's buffer hasn't been flushed at the end of the operation - since there's no using block, or the Close() call - which would flush automatically.

You need to call:

streamWriter.Flush();

before MVC takes over the stream.

Here's how your method should look like:

 public static Stream ATxt(IEnumerable<ArticuloBusquedaDTO> articles)
 {
    var stream = new MemoryStream();
    var streamWriter = new StreamWriter(stream, Encoding.UTF8);
    foreach (ArticuloBusquedaDTO article in articles)
    {
        streamWriter.WriteLine(article.ToStringFix());
    }
    // Flush the stream writer buffer
    streamWriter.Flush();
    stream.Seek(0, SeekOrigin.Begin);
    return stream;
}

Hope this helps!

Miroslav

Miroslav Popovic
Great! That was it. Thanks.
Carles