Reputation: 179
I have a button in my web page that will export CSV files. There are 5 files in total. When the client clicks the button, the server will create the files, compress them into one ZIP file, then send the ZIP file to the client for download.
I have heard around the forums about SharpZipLab
and DotNetZip
, but I haven't explored any yet. I have also heard using System.IO.Compression
. Which of these methods would you recommend?
I have this code to create the 5 CSV files:
StringBuilder sb = new StringBuilder();
DataTable[] dtCSV =
{
file1BLO.SelectFile1ForCSV(),
file2BLO.SelectFile2ForCSV(),
file3BLO.SelectFile3ForCSV(),
file4BLO.SelectFile4ForCSV(),
file5BLO.SelectFile5ForCSV()
};
for (int i = 0; i <= 4; i++)
{
DataTable dt = dtCSV[i];
foreach (DataRow dr in dt.Rows)
{
string[] fields = dr.ItemArray.Select(field => field.ToString()).ToArray();
sb.AppendLine(string.Join("|", fields));
}
Response.ContentType = "application/text";
Response.AddHeader("content-disposition", "attachment;filename=CAPRES-FILE" +
(i + 1) + "-" + DateTime.Now.ToString("yyyyMMdd-HHmmss") + ".txt");
Response.Output.Write(sb);
Response.Flush();
sb.Clear();
}
Response.End();
EDIT I'm using ASP.NET v4.0.
EDIT 2 Apparently I have System.IO.Compression
, which is weird because I though it is only supported in v4.5. Coincidentally, I don't have System.IO.Packaging
.
Upvotes: 0
Views: 3797
Reputation: 179
With the help of Sachu, we were able to accomplish this requirement. We used DotNetZip
over SharpZipLib
due to its licensing issues.
In facilitate our development of this functionality, I ought to create a program flow based on my requirements:
Zip
formatResponse
Before we start the process, we must prepare the project. This include adding necessary folders and instantiate variables.
First we add a folder to which we will 'temporarily' add the text files. This folder will also be the one that will get compressed. I decided to create the folder in the root directory of the project with the name CSV
.
Now we'll be using the DotNetZip
library. You can download it here. Add the library to your project references. Then add the using, which is using Ionic.Zip;
.
Then we instantiate the variables such as the zipFileName
, textFileName
, etc. The names speak for themselves.
The data that I'll be using for the text files will be from the DataTable[]
array, which each DataTable
corresponding to a specific SQL query.
DataTable[] dtCSV =
{
file1BLO.SelectFile1ForCSV(),
file2BLO.SelectFile2ForCSV(),
file3BLO.SelectFile3ForCSV(),
file4BLO.SelectFile4ForCSV(),
file5BLO.SelectFile5ForCSV()
};
StringBuilder sb = new StringBuilder();
string textFileNameTemplate = Server.MapPath(@"~\CSV") + @"\file";
Response.Clear();
Response.BufferOutput = false;
Response.ContentType = "application/zip";
Response.AppendHeader("content-disposition", "attachment;filename=CAPRES-" +
DateTime.Now.ToString("yyyyMMdd-HHmmss") + ".zip");
This is fairly easy. I used a StringBuilder
to convert the results from the DataTables
. Using this, I then used a StreamWriter
to build the text files themselves.
for (int i = 0; i <= 4; i++)
{
DataTable dt = dtCSV[i];
foreach (DataRow dr in dt.Rows)
{
string[] fields = dr.ItemArray.Select(field => field.ToString()).ToArray();
sb.AppendLine(string.Join("|", fields));
}
string textFileName = textFileNameTemplate + (i + 1) + ".txt";
var textFile = new StreamWriter(textFileName);
textFile.WriteLine(sb.ToString());
textFile.Flush();
textFile.Close();
}
Notice how I used the textFileNameTemplate
variable. I append the iterator and a .txt
file extension. Therefore, we will have files named file1.txt
, file2.txt
, file3.txt
, etc.
Now we can proceed with the zipping. We modified the code in Step 2 to accommodate the library.
using (ZipFile zip = new ZipFile()) //encapsulate Step 2 code in this code block
{
for (int i = 0; i <= 4; i++)
{
DataTable dt = dtCSV[i];
foreach (DataRow dr in dt.Rows)
{
string[] fields = dr.ItemArray.Select(field => field.ToString()).ToArray();
sb.AppendLine(string.Join("|", fields));
}
string textFileName = textFileNameTemplate + (i + 1) + ".txt";
var textFile = new StreamWriter(textFileName);
textFile.WriteLine(sb.ToString());
textFile.Flush();
textFile.Close();
sb.Clear();
zip.AddFile(textFileName, @"\"); //this is new
}
zip.Save(Response.OutputStream); //this is also new
}
Response.Flush();
Response.End();
zip.AddFile(textFileName, @"\");
adds the text file to an archive. The @"\"
means that DotNetZip
will not create subfolders that lead to the file, e.g. if my file is in this path: C:\User\Documents\...\file1.txt
, the archive would have a similar structure of folders. With @"\"
, the archive will only contain the text file.
Also take note of sb.Clear();
and its position in the code. It's important that it is inside the for loop but after the textFile.WriteLine(sb.ToString());
line. This makes sure that strings written before are cleared before looping again. This avoid carrying over strings from File1
to File2
, and File2
to File3
, and so on.
zip.Save(Response.OutputStream);
will directly output the Zip file to the Response
and does not save the file in the server.
This step depends on your requirements. For me, we will delete the generated files. Using System.IO.File
, we will delete the text files. After the using ZipFile zip = new ZipFile())
code block, we'll add the following lines:
for (int i = 1; i <= 5; i++)
{
File.Delete(textFileNameTemplate + i + ".txt");
}
My code probably isn't the most optimized code. But it works. If anyone can suggest a better code that would be great. But for now, I'll be using this code. Many thanks! Especially to Sachu, a really helpful person.
Upvotes: 1