Dynamic CSV file download - c#

In an MVC project, I have an ActionLink, which, when clicked, should take an arbitrary number of objects associated with the user (supplied as a List), dynamically build a CSV file of the objects' attributes and prompt a download. The file shouldn't persist on the server, so it either needs to be removed after download or the download needs to come from a stream or similar. What's the neatest way of going about this? I've seen examples of this which manually compile a CSV string and use HttpResponse.Write(String) within the controller, but is this best practice?

I have a function that is similar to this. I'm sure there is a "better" way to automatically find each of the members of the User object you pass it, but this way works.
public ActionResult ExportCSV(List<User> input)
{
using (MemoryStream output = new MemoryStream())
{
using (StreamWriter writer = new StreamWriter(output, Encoding.UTF8))
{
foreach (User user in input)
{
writer.Write(user.firstattribute);
writer.Write(",");
writer.Write(user.secondattribute);
writer.Write(",");
writer.Write(user.thirdattribute);
writer.Write(",");
writer.Write(user.lastattribute);
writer.WriteLine();
}
writer.Flush();
}
output.Position = 0;
return Controller.File(output, "text/comma-separated-values", "report.csv");
}
}

If you have the stream, returning a FileStreamResult is probably the "cleanest" way to do it in ASP.NET MVC.

You should manually assemble a string, then return Content(str, "text/csv");

LINQ to CSV is reportedly a nice library for generating CSV content. Once you generate your CSV, use the File method of Controller to return a FileStreamResult from your action; that lets you send any arbitrary stream as a response, whether it's a FileStreamResult or any other type of Stream.

Related

How to dynamically generate file for download in Razor Pages

I want to add a button that will download a dynamically generated CSV file.
I think I need to use FileStreamResult (or possibly FileContentResult) but I have been unable to find an example that shows how to do this.
I've seen examples that create a physical file, and then download that. But my ideal solution would write directly to the response stream, which would be far more efficient than creating a file or first building the string in memory.
Has anyone seen an example of dynamically generating a file for download in Razor Pages (not MVC)?
So here's what I came up with.
Markup:
<a class="btn btn-success" asp-page-handler="DownloadCsv">
Download CSV
</a>
Handler:
public IActionResult OnGetDownloadCsv()
{
using MemoryStream memoryStream = new MemoryStream();
using CsvWriter writer = new CsvWriter(memoryStream);
// Write to memoryStream using SoftCircuits.CsvParser
writer.Flush(); // This is important!
FileContentResult result = new FileContentResult(memoryStream.GetBuffer(), "text/csv")
{
FileDownloadName = "Filename.csv""
};
return result;
}
This code works but I wish it used memory more efficiently. As is, it writes the entire file contents to memory, and then copies that memory to the result. So a large file would exist twice in memory before anything is written to the response stream. I was curious about FileStreamResult but wasn't able to get that working.
If someone can improve on this, I'd gladly mark your answer as the accepted one.
UPDATE:
So I realized I can adapt the code above to use FileStreamResult by replacing the last block with this:
memoryStream.Seek(0, SeekOrigin.Begin);
FileStreamResult result = new FileStreamResult(memoryStream, "text/csv")
{
FileDownloadName = "Filename.csv"
};
return result;
This works almost the same except that, instead of calling memoryStream.GetBuffer() to copy all the bytes, it just passes the memory stream object. This is an improvement as I am not needlessly copying the bytes.
However, the downside is that I have to remove my two using statements or else I'll get an exception:
ObjectDisposedException: Cannot access a closed Stream.
Looks like it's a trade off between copying the bytes an extra time or not cleaning up my streams and CSV writer.
In the end, I'm able to prevent the CSV writer from closing the stream when it's disposed, and since MemoryStream does not have unmanaged resources there should be no harm in leaving it open.

C#: Best optimized way for reading contents of a JS and CSS files

Using C#, Framework 2.0
What is the best optimized way for reading contents of a JS and CSS files?
Note that the contents needs to write out at the same time
EDIT:
To be more clear, while loading the page I need to read all my JS file's contents and write out the contents on a page. Finally this page will act as a single JS file and in the same way for CSS files too.
Since here i am requesting to read the contents of a file number of times.
I am looking for the best way to optimize the performance of it while accessing the file.
As far as I know this is the best way to read the contents of a file,
Use File.ReadAllText(filepath) to get the contents of it.
filepath = Server.MapPath(jsFile);
if (File.Exists(filepath))
{
contents = File.ReadAllText(filepath);
Response.Write(contents);
}
Depends on the kind of project, but you can take a look at Bundling & Minification. That way you don't have to reinvent the wheel.
What is the goal? Scan and output? Here's a version that is encoding-safe (ie: does encoding translation when possible)
void ConcatenateAllSafe(StreamWriter output, params string[] files)
{
foreach (var f in files)
{
using(StreamReader rd = new StreamReader(f))
{
string line = null;
while (null != (line = rd.ReadLine()))
{
output.WriteLine(line);
}
}
}
}
// This version will assume everything is sharing the same encoding
void ConcatenateAllUnsafe(Stream output, params string[] files)
{
var data = new byte[1024 * 64];//64k should be enough for anyone
foreach (string f in files)
{
using (Stream inStream = new FileStream(f, FileMode.Open, FileAccess.Read, FileShare.Read))
{
for (int offset = 0,
read; 0 != (read = inStream.Read(data, offset, data.Length)); offset += read)
{
output.Write(data, 0, read);
}
}
}
}
There is already a ToolkitScriptManager, which will bundle, minify & compress all your javascripts into one request. You may try taking a look into this.
I understand your reason to bundle, minify & compress the javascript and CSS into one file, but fails to understand why it has to be done for every requests. You can either,
Use one of the existing Bundling and Minification tool as suggested by CodeMaster in his answer (YUICompressor, Closure Compiler, AJAXminifier). This would bundle the scripts and CSS only once, that too during build time itself and uses that to serve all the requests. To me this is the most effective way to bundle the scripts and reduces the load on server as they are simply static files and not processing is required for bundling them. Also, the scripts can be treated as static files and be cached at clients or loaded from CDN.
Use Toolkit Script Manager approach, where the custom handler would look for the query string, Look in its cache if the combination is present, if not build the output using Florion's answer and cache it for subsequent calls. By this way, you can avoid frequent file reads.

C# Access text file in zip archive

How can I read content of a text file inside a zip archive?
For example I have an archive qwe.zip, and insite it there's a file asd.txt, so how can I read contents of that file?
Is it possible to do without extracting the whole archive? Because it need to be done quick, when user clicks a item in a list, to show description of the archive (it needed for plugin system for another program). So extracting a whole archive isn't the best solution... because it might be few Mb, which will take at least few seconds or even more to extract... while only that single file need to be read.
You could use a library such as SharpZipLib or DotNetZip to unzip the file and fetch the contents of individual files contained inside. This operation could be performed in-memory and you don't need to store the files into a temporary folder.
Unzip to a temp-folder take the file and delete the temp-data
public static void Decompress(string outputDirectory, string zipFile)
{
try
{
if (!File.Exists(zipFile))
throw new FileNotFoundException("Zip file not found.", zipFile);
Package zipPackage = ZipPackage.Open(zipFile, FileMode.Open, FileAccess.Read);
foreach (PackagePart part in zipPackage.GetParts())
{
string targetFile = outputDirectory + "\\" + part.Uri.ToString().TrimStart('/');
using (Stream streamSource = part.GetStream(FileMode.Open, FileAccess.Read))
{
using (Stream streamDestination = File.OpenWrite(targetFile))
{
Byte[] arrBuffer = new byte[10000];
int iRead = streamSource.Read(arrBuffer, 0, arrBuffer.Length);
while (iRead > 0)
{
streamDestination.Write(arrBuffer, 0, iRead);
iRead = streamSource.Read(arrBuffer, 0, arrBuffer.Length);
}
}
}
}
}
catch (Exception)
{
throw;
}
}
Although late in the game and the question is already answered, in hope that this still might be useful for others who find this thread, I would like to add another solution.
Just today I encountered a similar problem when I wanted to check the contents of a ZIP file with C#. Other than NewProger I cannot use a third party library and need to stay within the out-of-the-box .NET classes.
You can use the System.IO.Packaging namespace and use the ZipPackage class. If it is not already included in the assembly, you need to add a reference to WindowsBase.dll.
It seems, however, that this class does not always work with every Zip file. Calling GetParts() may return an empty list although in the QuickWatch window you can find a property called _zipArchive that contains the correct contents.
If this is the case for you, you can use Reflection to get the contents of it.
On geissingert.com you can find a blog article ("Getting a list of files from a ZipPackage") that gives a coding example for this.
SharpZipLib or DotNetZip may still need to get/read the whole .zip file to unzip a file. Actually, there is still method could make you just extract special file from the .zip file without reading the entire .zip file but just reading small segment.
I needed to have insights into Excel files, I did it like so:
using (var zip = ZipFile.Open("ExcelWorkbookWithMacros.xlsm", ZipArchiveMode.Update))
{
var entry = zip.GetEntry("xl/_rels/workbook.xml.rels");
if (entry != null)
{
var tempFile = Path.GetTempFileName();
entry.ExtractToFile(tempFile, true);
var content = File.ReadAllText(tempFile);
[...]
}
}

.Net MVC Return file and then delete it

I've produced a MVC app that when you access /App/export it zips up all the files in a particular folder and then returns the zip file. The code looks something like:
public ActionResult Export() {
exporter = new Project.Exporter("/mypath/")
return File(exporter.filePath, "application/zip", exporter.fileName);
}
What I would like to do is return the file to the user and then delete it. Is there any way to set a timeout to delete the file? or hold onto the file handle so the file isn't deleted till after the request is finished?
Sorry, I do not have the code right now...
But the idea here is: just avoid creating a temporary file! You may write the zipped data directly to the response, using a MemoryStream for that.
EDIT Something on that line (it's not using MemoryStream but the idea is the same, avoiding creating a temp file, here using the DotNetZip library):
DotNetZip now can save directly to ASP.NET Response.OutputStream.
I know this thread is too old , but here is a solution if someone still faces this.
create temp file normally.
Read file into bytes array in memory by System.IO.File.ReadAllBytes().
Delete file from desk.
Return the file bytes by File(byte[] ,"application/zip" ,"SomeNAme.zip") , this is from your controller.
Code Sample here:
//Load ZipFile
var toDownload = System.IO.File.ReadAllBytes(zipFile);
//Clean Files
Directory.Delete(tmpFolder, true);
System.IO.File.Delete(zipFile);
//Return result for download
return File(toDownload,"application/zip",$"Certificates_{rs}.zip");
You could create a Stream implementation similar to FileStream, but which deletes the file when it is disposed.
There's some good code in this SO post.

webservice to return pdf with asp.net

I have a html to pdf conversion tool which resides on our server that I can access using a url with querystrings.
Something like myurl.com/createpdf.aspx?page=http://www.somesite.com.
The tool then converts the web page and displays it as a pdf.
I would like to offer this functionality as a web service, so that our clients can access this programmatically.
Ideal case would be, that our clients send in the site they would like to have converted and the web service then returns the pdf.
Would that make sense?
What would be the best way to implement this?
cheers,
Terry
Use file IO to read the PDF in and have your webmethod return it as a byte[] array. For example like this:byte[] filebytes = null;
using (FileStream fs = File.OpenRead(fileName))
{
filebytes = new byte[fs.Length];
fs.Read(filebytes, (int)0, (int)fs.Length);
}
return filebytes;
It will be Base64 encoded and you will then be able to Save and view the file on the client.
You could also write an http-handler that returns the pdf with the appropriate mime type. This way you would not have all the processing overhead of an aspx page.

Categories