How to get contents of System.Net.Mail.Attachment - c#

I have a System.Net.Mail.Attachment object with some .csv data in it. I need to save the contents of the attachment in a file. I tried this:
var sb = new StringBuilder();
sb.AppendLine("Accounts,JOB,Usage Count");
sb.AppendLine("One,Two,Three");
sb.AppendLine("One,Two,Three");
sb.AppendLine("One,Two,Three");
var stream = new MemoryStream(Encoding.ASCII.GetBytes(sb.ToString()));
//Add a new attachment to the E-mail message, using the correct MIME type
var attachment = new Attachment(stream, new ContentType("text/csv"))
{
Name = "theAttachment.csv"
};
var sr = new StreamWriter(#"C:\Blah\Look.csv");
sr.WriteLine(attachment.ContentStream.ToString());
sr.Close();
But the file has only the following: "System.IO.MemoryStream".
Could you please tell me how I can get the real data there?
Thanks.

You can't call ToString on an arbitrary stream. Instead you should use CopyTo:
using (var fs = new FileStream(#"C:\temp\Look.csv", FileMode.Create))
{
attachment.ContentStream.CopyTo(fs);
}
Use this to replace the last three lines of your example. By default, ToString just returns that name of the type unless the class overrides ToString. ContentStream is just the abstract Stream (at runtime it is a MemoryStream), so there is just the default implementation.
CopyTo is new in .NET Framework 4. If you aren't using the .NET Framework 4, you can mimic it with an extension method:
public static void CopyTo(this Stream fromStream, Stream toStream)
{
if (fromStream == null)
throw new ArgumentNullException("fromStream");
if (toStream == null)
throw new ArgumentNullException("toStream");
var bytes = new byte[8092];
int dataRead;
while ((dataRead = fromStream.Read(bytes, 0, bytes.Length)) > 0)
toStream.Write(bytes, 0, dataRead);
}
Credit to Gunnar Peipman for the extension method on his blog.

Assuming your stream isn't too big you can just write it all to the file like so:
StreamWriter writer = new StreamWriter(#"C:\Blah\Look.csv");
StreamReader reader = new StreamReader(attachment.ContentStream);
writer.WriteLine(reader.ReadToEnd());
writer.Close();
If it is bigger you probably want to chunk the reads up into a loop as to not demolish your RAM (and risk out of memory exceptions).

Related

iText 7 - HTML to PDF write to MemoryStream instead of file

I'm using iText 7, specifically the HtmlConverter.ConvertToDocument method, to convert HTML to PDF. The problem is, I would really rather not create a PDF file on my server, I'd rather do everything in memory and just send it to the users browser so they can download it.
Could anyone show me an example of how to use this library but instead of writing to file write to a MemoryStream so I can send it directly to the browser?
I've been looking for examples and all I can seem to find are those which refer to file output.
I've tried the following, but keep getting an error about cannot access a closed memory stream.
public FileStreamResult pdf() {
using (var workStream = new MemoryStream())
using (var pdfWriter = new PdfWriter(workStream)) {
pdfWriter.SetCloseStream(false);
using (var document = HtmlConverter.ConvertToDocument(html, pdfWriter)) {
//Returns the written-to MemoryStream containing the PDF.
byte[] byteInfo = workStream.ToArray();
workStream.Write(byteInfo, 0, byteInfo.Length);
workStream.Position = 0;
return new FileStreamResult(workStream, "application/pdf");
}
//return new FileStreamResult(workStream, "application/pdf");
}
}
You meddle with the workStream before the document and pdfWriter have finished creating the result in it. Furthermore, the intent of your meddling is unclear, first you retrieve the bytes from the memory stream, then you write them back into it...?
public FileStreamResult pdf()
{
var workStream = new MemoryStream())
using (var pdfWriter = new PdfWriter(workStream))
{
pdfWriter.SetCloseStream(false);
using (var document = HtmlConverter.ConvertToDocument(html, pdfWriter))
{
}
}
workStream.Position = 0;
return new FileStreamResult(workStream, "application/pdf");
}
By the way, as you are essentially doing nothing special with the document returned by HtmlConverter.ConvertToDocument, you probably could use a different HtmlConverter method with less overhead in your code.
Generally this approach works
using (var ms = new MemoryStream())
{
//yourStream.Seek(0, SeekOrigin.Begin)
yourStream.CopyTo(ms);
}

Convert List<string> to a stream

The problem I have is that I have a CSV file full of records, that currently is being mapped to a strongly typed collection via the open source CsvHelper.CsvReader.GetRecords<T> method. It gets passed a GZIP stream which is built on a FileStream so is reading the stream from disk.
My suspicion is that the CsvHelper class when used with a FileStream is not very efficient as this load takes a long time. I want to try and load the raw file efficiently first just into memory, and then do the strong type mapping afterwards.
Unfortunately, the mapping class CsvHelper.CsvReader.GetRecords<T> accepts only a stream. I have managed to load the raw data into a List<string> very fast, however I now cannot figure out how to "streamify" this to pass to the mapper. Is this something I can do or is there another solution?
My code so far is
var fileStream = ...
var gzipStream = new GZipStream(fileStream, CompressionMode.Decompress);
var entries = new List<string>();
using (var unzip = new StreamReader(gzipStream))
while(!unzip.EndOfStream)
entries.Add(unzip.ReadLine());
Parse(??);
public IReadOnlyCollection<TRow> Parse(Stream stream)
{
Func<Stream> streamFactory = () => stream;
var results = ParseCsvWithConfig <TRow>(streamFactory, _configuration).AsReadOnly();
}
public static IEnumerable<T> ParseCsvWithConfig<T>(Func<Stream> streamFactory, CsvConfiguration configuration)
{
using (var stream = streamFactory())
{
var streamReader = new StreamReader(stream);
using (var csvReader = new CsvReader(streamReader, configuration ?? new CsvConfiguration()))
{
return csvReader.GetRecords<T>().ToList();
}
}
}
Skip the list altogether:
var fileStream = ...
var gzipStream = new GZipStream(fileStream, CompressionMode.Decompress);
var memoryStream = new MemoryStream();
gzipStream.CopyTo(memoryStream);
// call Parse on memorystream
Feel free to add using blocks where appropriate in your code.

Why does StreamWriter need to be open to access my MemoryStream?

I have some test code that's preparing a MemoryStream that will eventually be read by an object. Here's how I want to write it:
var manager = new LeaderboardImportManager(leaderboard);
var columnNames = manager.ColumnNames;
var stream = new MemoryStream();
using (var writer = new StreamWriter(stream))
{
writer.WriteLine(string.Join(",", columnNames));
foreach (var user in users)
{
var row = leaderboard.Metrics.Select(m => Faker.RandomNumber.Next().ToString()).ToList();
row.Insert(0, user.UserName);
writer.WriteLine(string.Join(",", row));
}
writer.Flush();
stream.Position = 0;
}
return stream;
But when I do it that way, my stream object becomes unreadable and my test fails, so I have to do it like this:
var manager = new LeaderboardImportManager(leaderboard);
var columnNames = manager.ColumnNames;
var stream = new MemoryStream();
var writer = new StreamWriter(stream);
writer.WriteLine(string.Join(",", columnNames));
foreach (var user in users)
{
var row = leaderboard.Metrics.Select(m => Faker.RandomNumber.Next().ToString()).ToList();
row.Insert(0, user.UserName);
writer.WriteLine(string.Join(",", row));
}
writer.Flush();
stream.Position = 0;
return stream;
This, of course, prevents me from being able to dispose of my StreamWriter object, which as I understand it, should definitely be disposed of.
Why does the StreamWriter need to remain open if I've flushed its contents to the MemoryStream object already?
I can think of some very inconvenient ways to work around this, but I'd like to know why it doesn't work the way I want it, and whether or not there's something I can do to make it work that way. Any advice is appreciated, thanks!
By default, the StreamWriter "owns" the stream it is passed and will close it when disposed. Use the constructor that has a leaveOpen boolean parameter. Set it to true to avoid closing the underlying Stream when the writer is disposed in your first example.
StreamWriter automatically closes the Stream if you don't tell it not to. Create it like this instead to leave the Stream open:
using (var writer = new StreamWriter(stream, System.Text.Encoding.UTF8, 1024, true))
Alternatively, if you don't wish to pass the extra arguments, use GetBuffer to access MemoryStream's internal buffer. Avoid ToArray as this creates a copy of the data and depending on your scenario may be inefficient.
using operator calls Dispose method on object before exiting the operator block { }.
On disposal, StreamWriter disposes underlying Stream too.
This means your Stream is an invalidated object before it gets returned.
Apply using statement only for objects created and destroyed in current scope (do not return them at least).
Why does the StreamWriter need to remain open if I've flushed its
contents to the MemoryStream object already?
As #mikez mentioned, by default created StreamWriter "owns" the underlying stream, but you can avoid this behaviour by adding leaveOpen = true in constructor.
new StreamWriter(stream = s, encoding = Encoding.UTF8, bufferSize = 128, leaveOpen = true)
The other answers indicate I should use the constructor that has a leaveOpen param and set it to true, but I dislike this because the constructor also requires a bufferSize argument.
However, I realized that I can get away with this just as easily:
// new method body, returns byte array
var stream = new MemoryStream();
using (var writer = new StreamWriter(stream))
{
writer.WriteLine(string.Join(",", columnNames));
foreach (var user in users)
{
var row = leaderboard.Metrics.Select(m => Faker.RandomNumber.Next().ToString()).ToList();
row.Insert(0, user.UserName);
writer.WriteLine(string.Join(",", row));
}
writer.Flush();
stream.Position = 0;
}
return stream.ToArray();
// consumer opens a new stream using the bytes
using (var stream = new MemoryStream(this.GetCSVStream(leaderboard, users)))
{
mockFile.Setup(f => f.InputStream).Returns(stream);
this.service.UpdateEntries(update.ID, mockFile.Object);
}

Convert string to filestream in c#

Just started with writing unit tests and I am now, blocked with this situation:
I have a method which has a FileStream object and I am trying to pass a "string" to it.
So, I would like to convert my string to FileStream and I am doing this:
File.WriteAllText(string.Concat(Environment.ExpandEnvironmentVariables("%temp%"),
#"/test.txt"), testFileContent); //writes my string to a temp file!
new FileStream(string.Concat(Environment.ExpandEnvironmentVariables("%temp%"),
#"/test.txt"), FileMode.Open) //open that temp file and uses it as a fileStream!
close the file then!
But, I guess there must be some very simple alternative to convert a string to a fileStream.
Suggestions are welcome! [Note there are other answers to this question in stackoverflow but none seems to be a straight forward solution to that]
Thanks in advance!
First of all change your method to allow Stream instead of FileStream. FileStream is an implementation which, as I remember, does not add any methods or properties, just implement abstract class Stream. And then using below code you can convert string to Stream:
public Stream GenerateStreamFromString(string s)
{
MemoryStream stream = new MemoryStream();
StreamWriter writer = new StreamWriter(stream);
writer.Write(s);
writer.Flush();
stream.Position = 0;
return stream;
}
As FileStream class provides a stream for a file and hence it's constructor requires the path of the file,mode, permission parameter etc. to read the file into stream and hence it is used to read the text from file into stream. If we need to convert string to stream first we need to convert string to bytes array as stream is a sequence of bytes. Below is the code.
//Stream is a base class it holds the reference of MemoryStream
Stream stream = new MemoryStream();
String strText = "This is a String that needs to beconvert in stream";
byte[] byteArray = Encoding.UTF8.GetBytes(strText);
stream.Write(byteArray, 0, byteArray.Length);
//set the position at the beginning.
stream.Position = 0;
using (StreamReader sr = new StreamReader(stream))
{
string strData;
while ((strData= sr.ReadLine()) != null)
{
Console.WriteLine(strData);
}
}

How do I return a MemoryStream docx file MVC?

I have a docx file that I would like to return after I make edits. I have the following code...
object useFile = Server.MapPath("~/Documents/File.docx");
object saveFile = Server.MapPath("~/Documents/savedFile.docx");
MemoryStream newDoc = repo.ChangeFile(useFile, saveFile);
return File(newDoc.GetBuffer().ToArray(), "application/docx", Server.UrlEncode("NewFile.docx"));
The file seems fine, but I am getting error messages ("the file being corrupt" and another stating "Word found unreadable content. If you trust the source click Yes"). Any ideas?
Thanks in advance
EDIT
This is the ChangeFile in my Model...
public MemoryStream ChangeFile(object useFile, object saveFile)
{
byte[] byteArray = File.ReadAllBytes(useFile.ToString());
using (MemoryStream ms = new MemoryStream())
{
ms.Write(byteArray, 0, (int)byteArray.Length);
using (WordprocessingDocument wordDoc = WordprocessingDocument.Open(ms, true))
{
string documentText;
using (StreamReader reader = new StreamReader(wordDoc.MainDocumentPart.GetStream()))
{
documentText = reader.ReadToEnd();
}
documentText = documentText.Replace("##date##", DateTime.Today.ToShortDateString());
using (StreamWriter writer = new StreamWriter(wordDoc.MainDocumentPart.GetStream(FileMode.Create)))
{
writer.Write(documentText);
}
}
File.WriteAllBytes(saveFile.ToString(), ms.ToArray());
return ms;
}
}
I use a FileStreamResult:
var cd = new System.Net.Mime.ContentDisposition
{
FileName = fileName,
// always prompt the user for downloading, set to true if you want
// the browser to try to show the file inline
Inline = false,
};
Response.AppendHeader("Content-Disposition", cd.ToString());
return new FileStreamResult(documentStream, "application/vnd.openxmlformats-officedocument.wordprocessingml.document");
Don't use MemoryStream.GetBuffer().ToArray() use MemoryStream.ToArray().
The reason why is GetBuffer() relates to the array used to create the memory stream and not the actual data in the memory stream. The underlaying array could actually differ in size.
Hidden on MSDN:
Note that the buffer contains allocated bytes which might be unused.
For example, if the string "test" is written into the MemoryStream
object, the length of the buffer returned from GetBuffer is 256, not
4, with 252 bytes unused. To obtain only the data in the buffer, use
the ToArray method; however, ToArray creates a copy of the data in
memory.

Categories

Resources