0

My ultimate goal is to upload an rtf document and store the binary contents in a table, then retrieve the contents of that template, replace some text based on some pre-defined tags, and trigger the browser to download the updated template. My initial goal is to upload a rtf file (working), run it through my parsing method (with no changes), and have the downloaded file open successfully with the original template. Right now everything below is working, except the file produced is corrupt (and I'm not sure that my streams are being disposed of properly). I should probably dig into System.IO to better understand memory streams, etc., but this is the only place in this project where I need to do anything like this, so I'm trying to find a quick solution.

public static Stream ParseFile(Stream fsTemplate)
    {
        // create MemoryStream to copy template into and modify as needed
        MemoryStream doc = new MemoryStream();

        // copy template FileStream into document MemoryStream
        fsTemplate.CopyTo(doc);
        doc.Position = 0;

        string docText = string.Empty;
        using (StreamReader reader = new StreamReader(doc))
        {
            // doctText appears to look how I'd expect when I debug
            docText = reader.ReadToEnd();
            reader.Close();
        }

        //replace text in 'docText' as neccessary (not yet implemented)

        // This is where I'm not really clear what I should be doing differently
        var ms = new MemoryStream();
        StreamWriter writer = new StreamWriter(ms);
        writer.Write(docText);
        ms.Position = 0;

        return ms;
    }

Here's my controller, which retrieves binary data (uploaded rtf template) from the database, calls the above method, and returns the file stream to the browser

public FileStreamResult Parse(int id)
    {
        byte[] data = Context.Templates.Find(id).BinaryData;
        Stream stream = new MemoryStream(data);

        var updatedStream = Tools.ParseFile(stream);

        return File(updatedStream, "application/rtf", "temp.rtf");
    }

I replaced my StreamWriter with this block of code, and now it's working how I expected. I'm still not sure if I'm disposing of my streams properly though.

byte[] buffer = Encoding.ASCII.GetBytes(docText);
        MemoryStream ms = new MemoryStream(buffer);

Full code:

public static Stream ParseFile(Stream fsTemplate)
    {
        // create MemoryStream to copy template into and modify as needed
        MemoryStream doc = new MemoryStream();

        // copy template FileStream into document MemoryStream
        fsTemplate.CopyTo(doc);
        doc.Position = 0;

        string docText = string.Empty;
        using (StreamReader reader = new StreamReader(doc))
        {
            // doctText appears to look how I'd expect when I debug
            docText = reader.ReadToEnd();
            reader.Close();
        }

        //replace text in 'docText' as neccessary (not yet implemented)

        byte[] buffer = Encoding.ASCII.GetBytes(docText);
        MemoryStream ms = new MemoryStream(buffer);

        return ms;
    }
Josh
  • 1,876
  • 3
  • 21
  • 35
  • can you confirm if fsTemplate position was at 0 before you called CopyTo method – adeel41 Apr 19 '17 at 17:33
  • Does it get initialized at 0? This is where I'd be creating it and passing it into the Parse method: Stream stream = new MemoryStream(data); – Josh Apr 19 '17 at 17:36

2 Answers2

0

I would suggest to call writer.Flush() to force it for writing buffered data.Or envelop writer instancination with "using" which will do the same finally.

public static Stream Parse(Stream fsTemplate){...    

// This is where I'm not really clear what I should be doing differently
var ms = new MemoryStream();
StreamWriter writer = new StreamWriter(ms);
writer.Write(docText);
writer.Flush();  // <-- here
ms.Position = 0;
0

Here is how it is done.

Create the rtf document and save it as say AAnRTF.txt The contents:

<div>
This is an rtf document
<div>

Here is your controller and methods:

public class HomeController : Controller
{
    public ActionResult UploadFilePost(string SubmitButton, HttpPostedFileBase file)
    {
        if (SubmitButton == "Upload RTF")
        {
            //I use BreazEntities13, you will use what comes out of the EDMX wizard
            //EDMX is easy please try it
            byte[] byteContent;
            Stream aStream = file.InputStream;
            MemoryStream memoryStream = new MemoryStream();
            aStream.CopyTo(memoryStream);
            byteContent = memoryStream.ToArray();

            ImportMod imsk = new ImportMod
            {
                Contents = byteContent,
                ContentType = "text/plain"
            };
            using (BreazEntities13 entity = new BreazEntities13())
            {
                entity.ImportMods.Add(imsk);
                entity.SaveChanges();
            }
        }
        else if (SubmitButton == "Download RTF")
        {
            //get documnet
            ImportMod kit = new ImportMod();
            using (BreazEntities13 entity = new BreazEntities13())
            {
                kit = entity.ImportMods.FirstOrDefault();  //! GETS only the first documument
            }

            //modify file -credit to other stack o pages
            //eg http://stackoverflow.com/questions/9174402/replacing-the-innertext-of-an-xml-node-element
            XmlDocument doc = new XmlDocument();
            string xml = Encoding.UTF8.GetString(kit.Contents);
            doc.LoadXml(xml);
            doc.SelectSingleNode("div").InnerText = "Replace text, xmldocument is similiar to rtf or choose a diff type";
            byte[] bytes = Encoding.Default.GetBytes(doc.OuterXml);

            this.ControllerContext.HttpContext.Response.ClearContent();
            this.ControllerContext.HttpContext.Response.ContentType = "text/plain";
            this.ControllerContext.HttpContext.Response.AddHeader("content-disposition",
            "attachment; filename=" + "AAnRTF.txt");
            this.ControllerContext.HttpContext.Response.BinaryWrite(bytes);
            this.ControllerContext.HttpContext.Response.End();
        }

        return View("UploadFile");
    }

    public ActionResult UploadFile()
    {
        return View();
    }

Create a table to hold the file:

GO

/****** Object:  Table [dbo].[ImportModStarterKit]    Script Date: 4/19/2017 10:25:48 AM ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

SET ANSI_PADDING ON
GO

CREATE TABLE [dbo].[ImportMod](
    [Id] [int] IDENTITY(1,1) NOT NULL,
    [Contents] [varbinary](max) NULL,
    [ContentType] [nvarchar](2048) NULL,
 CONSTRAINT [PK_ImportModStarterKit] PRIMARY KEY CLUSTERED 
(
    [Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]

GO

SET ANSI_PADDING OFF
GO

Create an edmx ADO.NET ENTITY DATA MODEL to this table

Here is your view:

@{
    Layout = null;
}

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>UploadFile</title>
</head>
<body>
    <div>
        @using (Html.BeginForm("UploadFilePost",
        "Home",
        FormMethod.Post,
        new { enctype = "multipart/form-data" }))
        {
            <div class="commonImportPageHeight">
                Import Cases
                <div class="boxAroundFileUpload">
                    <input type="file" name="file" id="file" class="uploadFileWidth" /><br>
                    <input type="submit" name="SubmitButton" value="Upload RTF" />
                    <br/>
                    <input type="submit" name="SubmitButton" value="Download RTF" />
                    <br>
                </div>
            </div>

        }

    </div>
</body>
</html>
kblau
  • 2,094
  • 1
  • 8
  • 20
  • I appreciate your response, and this will likely be helpful to someone, but it wasn't really the issue I was trying to address. My upload and storage process was already established and working, but I was having trouble modifying the file and returning a non-corrupt, updated version of my file. I can't store anything to disk, even temporarily, so all must be done in memory. I updated my question to include code that is working and will incorporate Radislav Kireev's input. – Josh Apr 20 '17 at 10:32