Category Archives

40 Articles

Posted by .Ronald on

How to create a zip archive and download it in ASP.NET

In a previous post How to download multiple files in ASP.NET, I explained how to generate multiple documents and offer them as separate downloads in ASP.NET. One of the options I had when looking for a solution to offer multiple downloads, was adding all the documents to 1 single zip archive container, and offer that as download to the user. This solution didn’t completely satisfy the end-users, but is also offered for those who want to use it.

In this post I will explain, how I take the same list of documents, and offer them as a zip archive to download. Starting from the multiple download solution, this only required 1 extra step in the process, namely, creating a zip archive and adding all the documents to it. The rest of the process is as described in the previous post.

The method takes the same argument as when creating separate download links, namely an a list of byte arrays. Each byte array in its turn contains the binary content of the document. I use the SharpZipLib from ICSharpCode, which can be downloaded here: The Zip, GZip, BZip2 and Tar Implementation For .NET. This is what this method looks like:


Private Function ZipDocuments(ByVal reports As IList(Of byte())) As Boolean

' Add documents to 1 ZIP file, and open in browser
Using zipOutMemoryStream As New MemoryStream()
Using zipOutStream As New ZipOutputStream(zipOutMemoryStream)

'Add documents to Zip File.
Dim cnt As Integer = 1
For Each buffer As byte() In reports
Dim entry As New ZipEntry(String.Format("{0}_{1}.pdf", "GeneratedFile", cnt))

zipOutStream.PutNextEntry(entry)
zipOutStream.Write(buffer, 0, buffer.Length)
zipOutStream.CloseEntry()
cnt += 1
Next

zipOutStream.Finish()
zipOutStream.Close()
zipOutMemoryStream.Close()

Dim responseBytes As Byte() = zipOutMemoryStream.ToArray()

'Return Null on Empty Zip File
Const ZIP_FILE_EMPTY As Integer = 22
If responseBytes.Length <= ZIP_FILE_EMPTY Then
Return Nothing
End If

RegisterDocumentDownload(Guid.NewGuid().ToString(), responseBytes, ContentTypes.ZIP)

End Using
End Using
End Function

I first create a (binary) memorystream (zipOutMemoryStream) to contain the content of the zip file (zipOutStream).
Then I loop over the list of documents (or files), create an entry in the zip file (entry as ZipEntry), and write the binary content to the zip entry.
After adding the files to the zip and cleaning up, I can use the same RegisterDocumentDownload() method from the previous post, and the zip archive will be added to the user and opened in the browser.

And that’s it…

Posted by .Ronald on

How to download multiple files in ASP.NET

The project I’m currently assigned to, already has an option to generate reports (pdf) which are just streams the binary output of the report generator to the response output stream. Something like this:


Dim binReader As New System.IO.BinaryReader(report.ExportToStream())

With Response
.ClearContent()
.ClearHeaders()
.ContentType = "application/pdf"
.AddHeader("Content-Disposition", "inline; filename=AankondigingControlesEnGevolgen.pdf")
.BinaryWrite(strStream.ReadBytes(CInt(strStream.BaseStream.Length)))
.Flush()
.Close()
End With

This piece of code streams the binary output of the report to the response object, and setting the right ContentType and Header, it opens the document in the user’s browsers. Works like a charm.

But now I was asked to create a form where the user can select multiple reports to download and open them in the browser. My first answer was: We can’t do that (easily). But then I started to look at the options we have when working in ASP.NET and generating output to the client browser.

The solution I ended up with was that easy, that I found myself kind of stupid that I didn’t think about it earlier. This is what I did:

  1. Generate the documents and store them (binary), together with a unique key, in a session variable
  2. Generate download links with that unique key as parameter
  3. Open the links with clientside javascript
  4. In the download page, retrieve the content from the session variable and stream it to the client browser

Let’s take a look at that in detail.

1. Generate the documents and store them (binary), together with a unique key, in a session variable

I created a custom class to hold the binary document content, together with extra information that can be helpful when generating the download:


Private Class ContentTypes
Public Const PDF As String = "application/pdf"
Public Const ZIP As String = "application/zip"
End Class

<Serializable()> _
Private Class Download
Public Name As String
Public Content() As Byte
Public ContentType As String
End Class

Name: The name of the file that is generated and is used when the user downloads the file (save to disk)
Content: The binary content of the file
ContentType: Because I don’t want to be limited to 1 specific file type, I include the content type with the download

Currently I’m only using 2 types of documents, but as you can see, this can be easily extended.

2. Generate download links with that unique key as parameter

For each document I created and stored, together with a unique key, in a session variable, I generated the client-side script to open a new window with the download link. Because I use the same page to download the document, I can create a URL starting with the querystring question mark:


Private Sub RegisterDocumentDownload(ByVal key As String, ByVal content() As Byte, ByVal contentType As String)
Dim script As String = String.Format("window.open('?key={0}');", key)
Dim download As New Download()
download.Content = content
download.ContentType = contentType
download.Name = key

Session.Add(key, download)
ScriptManager.RegisterStartupScript(Me, Me.GetType(), "Download_" & key, script, True)
End Sub

3. Open the links with clientside javascript

The JavaScript that is generated, will look something like this (when generating 3 downloads):


<script type="text/javascript">
//<![CDATA[
window.open('?key=ee00cb06-81f6-48d7-bed2-6cf0af90d5f8');
window.open('?key=a05d2567-5ab8-4c41-a000-bb0bd16498ca');
window.open('?key=ea7bb0aa-6686-442e-be6f-b14ac14aacf5');
//]]>
</script>

4. In the download page, retrieve the content from the session variable and stream it to the client browser

Because I use the same page to download the file as well, I added code to the Page_Load() event that checks for the “key” parameter


If Not Request.QueryString("key") Is Nothing Then
StreamDownload(Request.QueryString("key"))
Exit Sub
End If

This calls the StreamDownload() method which takes the download from the session, streams the content to the browser client and cleans up everything before ending processing


Private Sub StreamDownload(ByVal key As String)

Guard.ArgumentNotNull(Session(key), "download")

Dim download As Download = DirectCast(Session(key), Download)
Dim stream As New MemoryStream()
Dim formatter As New BinaryFormatter()

formatter.Serialize(stream, Session(key))
With Response
.Clear()
.ContentType = download.ContentType

Select Case download.ContentType
Case ContentTypes.ZIP
.AppendHeader("Content-Disposition", String.Format("filename={0}.zip", download.Name))
End Select

.BinaryWrite(download.Content)
.Flush()
End With

' cleanup temporary objects
Session.Remove(key)
Session.Remove(key & "_download")

Response.End()

End Sub

As you can see, I also have the possibility to generate zip archives. This is to offer the functionality of downloading multiple documents in 1 zip archive container. I could easily immediately offer this zip download from within the page. But I prefer to use this generic solution, even if I’m only offering 1 file to download. This also gives me the possibility to offer other file formats as well. I just need to add a new content type, and alter the code where needed in the StreamDownload() method.

In a next post, I will show how I created 1 zip archive which contains 3 documents, and offer this as a download to the user.

Posted by .Ronald on

Introducing NuGet

What is NuGet?

clip_image001

From the NuGet website:

NuGet is a Visual Studio extension that makes it easy to install and update open source libraries and tools in Visual Studio.

Installing NuGet

Method 1: NuGet comes with ASP.Net MVC3

The easiest way would be to have Visual Studio 2010 (any version, even Visual Studio Express is supported) installed, NuGet comes with it. You can easily get ASP.Net MVC3 from the ASP.NET MVC website or with the Microsoft Web Platform Installer. But of course, you can also use NuGet if you’re not developing ASP.Net MVC3 applications.

Method 2: Using the Extension Manager

The second way you can obtain NuGet on your system is by using the Visual Studio Extension Manager. Search in the Online Gallery for the NuGet Package Manager and hit the “Download” button.

ScreenShot001

ScreenShot002

After a restart of Visual Studio the Library Package Manager is available in the Tools menu.

ScreenShot001

Method 3: From the NuGet website

It can hardly be easier, just go to the NuGet website, and click the “Install NuGet” button. This will lead you to the NuGet Package Manager on the Visual Studio Gallery MSDN site click the “Download” button to initiate the installer.

ScreenShot002

This will pop up the same installer as described in the previous method.

Managing Packages

There are 2 ways you can manage packages. The easiest way is by using the Add Library Package Reference GUI. The other way is by using PowerShell commands in the Package Manager Console. In either way you can achieve the most common tasks. The PowerShell method is useful when you don’t have a solution open or when you need commands that are only available as PowerShell commands.

Finding a package using the Add Library Package Reference Dialog

Right-click References in your project, select Add Library Package Reference and look online in the NuGet official package source for the library you want to add to your project.

ScreenShot003

After you hit the Install button, the library will be added to your project.

ScreenShot003

ELMAH (Error Logging Modules and Handlers) is an application-wide error logging facility that is completely pluggable. It can be dynamically added to a running ASP.NET web application, or even all ASP.NET web applications on a machine, without any need for re-compilation or re-deployment.

As you can see, there is a new reference to the Elmah binary added to our project. And if you open the web.config file, you will see that the package installer added the required configuration settings for you.

ScreenShot004

Note: OK, I cheated in the samples above. If you install the 1.2 version (latest at the moment of writing) from the repository, the web.config transformation was removed, and installed the 1.1 version instead using the Package Manager Console.

NuGet is also able to determine if your library has dependencies on other libraries and will install or upgrade them if they are not already installed. All on one single click Smile

ScreenShot004

ScreenShot005

Removing a package

Removing a package is as easy as opening the Add Library Package Reference screen, selecting the Installed packages and hitting the Uninstall button. Et voilà, NuGet does not only remove the libraries, but also cleans up the web.config for you.

ScreenShot005

Updating a package

After a while, you will notice that some packages have been updated in the repository. If you want the updated versions in your project, open up the Add Library Package Reference screen, select Updates and see which packages have updates available. Clicking the Update button of a package will update your project to the latest version.

Finding a package using the Package Manager Console

Anything you did before with the Add Library Package Reference GUI can also be done with the command line. Open up the Package Manager Console PowerShell, start typing a command and hit the TAB button. Yes, the Package Manager Console supports intellisense!

To search for a package, use the Get-Package command (with –Filter or you will get a list of all the packages in the repository):

ScreenShot007

To install a package, use the Install-Package command, followed by the package name. You can even use autocomplete in the package name!

ScreenShot008

Removing a package

How about trying the command Uninstall-Package followed by the package name. Would that uninstall the package?

ScreenShot009

Yes it does! Smile

Updating a package

Then would the command Update-Package update a package just like that?

ScreenShot010

Man, this is easy…

Packages folder

Now, to better understand what happens when we install a package, we can take a look in the packages folder that NuGet has created in our solution’s folder:

ScreenShot011

You can see that a package folder contains several subfolder of which the lib folder is the minimal requirement, that’s where your assembly dll is. In this example of the SqlServerCompact EntityFramework package, we have also a Content folder with an App_Start subfolder, which contains script that runs every time the application starts. And that’s also why the package has a dependency on the WebActivator package.

Any structure of folders and files can be placed in the Content folder, they will be copied in the same structure to your project’s folder. Use this when you want to include JavaScript, CSS, images, or any other file in the project.

This example also contains a tools folder where you can place PowerShell scripts that automatically run when the package is installed or removed.

When we take a look at the NewtonsoftJson package, we can see that the lib folder can also contain subfolders to support multiple .NET Framework versions and profiles. Although this is not a requirement, it can help to better target your package and optimize your code for different frameworks. The NuGet package installer automatically selects the correct version of the assembly in the package.

ScreenShot012

Note: The WebActivator package allows other packages to execute some startup code in web applications by creating an App_Start folder in your project and executing the code that’s inside it. The NuGet documentation details the requirements to use the WebActivator.

NuGet Package Explorer

Another way to look at packages is by using the NuGet Package Explorer application. This is a separate tool that can be downloaded from the NuGet CodePlex page at http://nuget.codeplex.com/releases/view/59864.

This tool gives you all the information that is in the package spec (metadata) file and the package content.

With the Package Explorer you can also edit the package metadata, if you’re not fond of editing the XML spec file. More on that in the next chapter about creating packages.

ScreenShot013

Creating and Publishing Packages

When you want to create and publish packages, you also have the choice between using a GUI, the Package Explorer, and using the command line.

Creating a NuGet package from the command line

First, we need to download the NuGet Command Line from http://nuget.codeplex.com/releases/view/58939, and make sure the NuGet.Exe is in our path.

Let’s create a really straightforward assembly:

ScreenShot014

Before we can create our package, we need to create a Package.spec file; this is the file that contains all the metadata of our package:

ScreenShot015

This is basically a template which you’ll have to modify by yourself. A better way would be to add an AssemblyInfo.cs file with your assembly metadata to your project and include it when compiling the assembly.

ScreenShot017

ScreenShot018

As you can see, you can add or link to a license or license file, a url for an icon, tags, dependencies to other packages and so on. Omit them if you don’t need them. Open it up with the Package Explorer to see the details.

When you build the package with the “nuget pack” command, we get a package file with the version number from the nuspec file, this allows us to differentiate every version we make.

Creating a NuGet package with the NuGet Package Explorer

Everything we did with the NuGet Command Line can also be done with the NuGet Package Explorer. Open up the NuGet Package Explorer, select File – New, and edit your package metadata and contents.

ScreenShot019

The result is the same package, created with another tool. Choose which one fits you best, the NuGet Command Line or the NuGet Package Explorer.

Exploring a NuGet package with the NuGet Package Explorer

A NuGet package is essentially a zip file, so you can extract it and explore its contents. When you extract a package, you see the nuspec file is also included. When you open it with the NuGet Package Explorer, you can see the metadata and contents of the package.

Take a look at the ELMA package:

ScreenShot021

ScreenShot023

File and source code transformations

Often we need to add code to our project, or execute some script after we referenced an assembly and before we can start using it, think of configuration in web.config, or add references to our project.

This is where NuGet gives us Source Code Transformations. The idea is to add a file to your package with just the transformations you need to make to the project’s source file, append it with “.transform” and place it in the Content folder, or any subfolder as it would sit in your project. NuGet than takes that file and merges it with any existing file.

For example the Elmah package needs to add some configuration sections and an HttpHandler to the web.config file. It does this by including a web.config.transform file with just these entries that it needs to add. NuGet will take this file and merge it with the application’s web.config file. Even nicer is that when you decide to remove the Elmah package, NuGet is able to clean up the web.config for you!

ScreenShot024

By adding PowerShell scripts to the Tools subfolder of you package, you can do about anything, not only add install or uninstall of your package, or when the application starts up, possibilities go far beyond that. A good example is the MvcScaffolding package, you should definitely add this and take a look at the package, there is really nice stuff in there.

Publishing your own NuGet packages

One way to publish your packages, besides of pushing them to the official NuGet server at http://nuget.org, is to setup a shared (network) folder. You can do this in the Package Manager Settings dialogue, under Package Manager – Package Sources. Now you have a new package source available when you open the Add Library Package Reference dialogue.

Another way is to create a new empty web application and add the NuGet.Server application. This will download all the required software to setup your own NuGet server. The packages you add to the Packages subfolder of this web application become available in the package feed when you start the application.

The NuGet.org server itself, is an instance of Orchard, so you could set it up in that way, refer to the extensive documentation of Orchard.

Links: