Version

Saving Files as Stream (WebUpload)

Topic Overview

Purpose

This topic explains how to process and save upload files as either file or memory streams. A detailed procedure is provided on saving files as memory stream by individual processing of each uploaded file chunk.

Required background

The following topics are prerequisites to understanding this topic:

Topic Purpose

This topic explains conceptually the WebUpload control and its features. It also demonstrates how to add the control to an ASPX page.

This topic explains, with code examples, how to configure the WebUpload control.

This topic demonstrates how to configure the HTTP Module and HTTP Handler to process the server events necessary to accept the data uploaded with the WebUpload control.

This topic explains how to handle the client-side events raised by the WebUpload control.

Saving Files as Stream – Conceptual Overview

Saving files as stream summary

When uploading files, you typically want to process them on the server. For example, you may want to save them to a database or to compress them before saving them to a file system.

The WebUpload control can work without any application settings explicitly defined. However, best practice is to always configure things like:

  • A folder for storing temporary files

This can be configure to save the file to an alternate folder. Left unconfigered, the files are stored in the default location; <project folder>\Uploads .

  • The file size limit

The maximum allowed size for the uploading files is set through the maxFileSizeLimit property. The default setting is 4 Mb.

  • The file saving mode

The file saving mode specifies how file processing is done – as a file stream or memory stream. (For details, refer to File Processing Overview.) The file saving mode is managed by the FileSaveType property whose allowed values are filestream and memorystream corresponding to the respective manners of file processing. The default setting is filestream .

In the ASPX page, you have to implement the method which will process each individual chunk of data of the file.

Steps

Following are the general conceptual steps for WebUpload save file as stream functionality.

1. Adding WebUpload control to the page

2. (Optional) Configuring WebUpload application settings

3. Implementing the file caching functionality

4. Handling WebUpload events

File Processing Overview

File processing summary

The WebUpload control provides the following ways to process uploaded files depending on whether files are processed on demand while uploading, or after they are saved to a temporary file on the server:

  • File stream processing – the uploaded files are first saved as temporary files by the WebUpload control. Once saved, you can process the files. Use this mode when you want to save the files on the server file system.

  • Memory stream processing – the uploaded files can be processed on demand either during the upload (and then saved) or after they are uploaded to memory. Use this mode when you need a finer control over the upload process, (When you want, for instance, to compress the file before saving it to a disk or a database.)

File stream processing

File stream mode uploads the file to a temporary location (indicated by fileUploadPath application setting) and keeps it there unless you choose to process it. In order to process the uploaded file, you need to handle UploadFinishing event. UploadFinishing event has a parameter of type FilesStream which gives you access to the uploaded file. Using this parameter you can, for example, move the file to another location. When processing the file manually, you must set the UploadFinishingEventArgs.Cancel property to true . This ensures that the temporary file will be deleted by Web Upload .)

If UploadFinishing event is not handled, then the temporary file is renamed using its original name overwriting any existing file with the same name and stays in the temporary folder.

Memory stream processing

When using memory stream processing, files are uploaded to MemoryStream objects that you can process manually. The WebUpload component offers the following approaches to uploading to MemoryStream objects (depending on which events you are handling and how you process those events.):

  • (Recommended) Process each chunk individually.

In order to be able to process file manually, you need to handle the FileUploading event. The FileUploading ’s FileUploadingEventArgs parameter has a FileChunk property of type byte array. (This property stores the current/last uploaded chunk of data.) FileUploading is a cancelable event so you can use it to return feedback to the WebUpload control, regardless of the file chunk having been processed or not. Whether or not you choose to cancel the event, depends on if you want to process the file chunks automatically or manually.

  • Manual processing of the file chunks

If you want to process the file chunks manually, you need to cancel the FileUploading event. In such cases, the WebUpload will not append the chunk into an internal MemoryStream object. This approach is covered in detail in Saving Files as Memory Stream by Processing Each Uploaded Chunk of the File Individually – Procedure)

  • Automatic processing of the file chunks

If you decide not to cancel FileUploading event, then the file chunk is appended to a MemoryStream object which will be available in the UploadFinishing event (UploadFinishingEventArgs.FileStream).

To handle the FileUploading event, use the AddFileUploadingEventHandler method of the UploadProgressManager class. UploadProgressManager is a singleton class an instance of which can be accessed by its Instance property .

  • Processing the whole file at once.

This is achieved by handling the UploadFinishing event. The UploadFinishing ’s UploadFinishingEventArgs parameter has a FileStream property which holds the uploaded file as a MemoryStream object. Use this property (UploadFinishingEventArgs.FileStream) to process the file data.

Note
Note:

This approach has the distinct disadvantage of keeping the file data in memory until the file is fully uploaded to the server.

Saving Files as Memory Stream by Processing Each Uploaded Chunk of the File Individually – Procedure

Introduction

This example configures WebUpload control using memory stream processing by handling the FileUploading event and processing each file chunk individually. Uploaded chunks are appended to an ASP.NET cache buffer. When the buffer reaches a certain threshold it is written to a file on disk. This strategy allows us to optimize WebUpload usage of RAM and thus handles more simultaneous file uploads.

This example creates a custom class in order to implement file caching functionality. UploadUtils is static class the purpose which is to handle currently uploading files into a MemoryStream dictionary in the cache. It also uses WebUpload ’s fileUploadPath application setting in order to write files to this directory.

Optionally, a folder for storing temporary files is created and the maximum size of the uploading files is set to 100 Mb.

Requirements

Following are the general requirements for saving a file as memory stream by processing each uploaded chunk of the file individually.

  • Microsoft® Visual Studio® 2010 or later

  • ASP.NET Framework

  • Infragistics Ignite UI® 13.1 or higher

Prerequisites

Overview

Following is a conceptual overview of the process:

1. Adding WebUpload control to an ASPX page

2. (Optional) Configuring WebUpload application settings

3. Implementing the file caching functionality

4. Handling WebUpload events

5. Verifing the result

Steps

The following steps demonstrate how to upload file as memory stream.

1. Add WebUpload control to an ASPX page.

1. Open the Default.aspx page in design view

2. From the Toolbox expand the section “ASP.NET 13.1 CLR 4.0”

Note
Note:

If you do not see the section then you probably did not add Ultimate UI for ASP.NET controls to the Toolbox. For additional information on fixing this refer to the “Running the Toolbox Utility” section of Ultimate UI for ASP.NET 2013.1 topic.

3. Drag and drop WebScriptManager control to the page.

4. Drag and drop WebUpload control to the page

A dialog “ ImportDefaultStyleSet ” may appear to inform you that the JavaScript and CSS files required by the control will be imported to the ig_ui directory. Press OK.

Note
Note:

If the dialog does not appear then you probably have disable it; however, the scripts and styles will be imported.

5. Add a div element which will show WebUpload errors.

We will show WebUpload errors in a div element. Add the following code after the WebUpload control code:

In HTML:

<div id="uploadErrors" style="color: red;"></div>

6. Add ig_ui directory to the solution

  1. Enable “Show All Files” – in the Solution Explorer window press the “Show All Files” button. You can now see the ig_ui directory.

  2. Right click on ig_ui directory and click “Include In Project” – the whole directory will be included in the project.

7. Add WebUpload script references to the Master Page

  1. Add style references

    1. Open Site.Master file

    2. Add the following style references to the Site.Master file.

In HTML:

<link href="ig_ui/css/structure/infragistics.css" rel="stylesheet" type="text/css" />
<link href="ig_ui/css/themes/infragistics/infragistics.theme.css" rel="stylesheet" type="text/css" />
  1. Add script references

    1. Open Site.Master file

    2. Add the following script references to the Site.Master file after the jQuery and jQuery UI scripts:

In HTML:

<script src="ig_ui/js/infragistics.js" type="text/javascript"></script>

8. (Optional) Build the site and run it

Right now you should have a working project; build and run it. Try to upload a file and see if it is in the Uploads folder.

If there is a problem you should check the browser error console for errors.

2. (Optional) Configure WebUpload application settings.

1. (Optional) Configure a temporary files folder.

The following code configures a folder for storing temporary files called Uploads .

In XML:

<appSettings>
    <add key="fileUploadPath" value="~/Uploads" />
</appSettings>

2. (Optional) Configure the file size limit.

The following code sets the maximum size of the files uploading files to 100 Mb.

In XML:

<appSettings>
    <!--100 Mb = 2097152 bytes-->
    <add key="maxFileSizeLimit" value="104857600" />
</appSettings>

3. Configure the file saving mode.

The following code sets the file saving mode to memorystream .

In XML:

<appSettings>
    <add key="FileSaveType" value="memorystream"/>
</appSettings>

3. Implement the file caching functionality.

To implement the caching functionality:

1. Configure the buffer size.

  1. Open the web.config file.

  2. Add the buffer size application setting.

In this example, the uploadUtilsBufferSize application setting configures the threshold for buffering file data before writing it to disk. Use this configuration setting to implement our own buffering of the file in the ASP.NET cache. The following code configures it to 2 Mb.

In XML:

<appSettings>
    <!--2 Mb = 2097152 bytes-->
    <add key="uploadUtilsBufferSize" value="2097152" />
</appSettings>

2. Add the UploadUtils file to the project.

  1. Right-click on the project.

  2. From the context menu, select Add and then Class…”. The Add New Item dialog opens.

  3. In the Add New Item dialog, Name field, type UploadUtils.cs.

  4. Press Add. The file is now added to the project.

3. Implement the UploadUtils class.

  1. Open the UploadUtils.cs file . All the code in the steps that follow is written in that file.

  2. Make the class static.

Add the static keyword to the UploadUtils class definition:

In C#:

public static class UploadUtils
{
    // Class definition here see steps 2 to 7
}
  1. Add the using clauses.

The using clauses are needed by the ConfigurationManager and Path classes. In the following code the first using clause is for the ConfigurationManager class and the second is for the Path class:

In C#:

using System.Configuration;
using System.IO;
  1. Configure the constants.

The constants are necessary when the web.config file does not contain application definitions for the uploadUtilsBufferSize and fileUploadPath settings.

In C#:

//4 Mb default buffer size
private const int DEFAULT_BUFFER_SIZE = 4 * 1024 * 1024;
private const string DEFAULT_UPLOAD_PATH = "~/Uploads";
  1. Configure the buffer size.

Define the static BufferSize property. This property returns the uploadUtilsBufferSize application setting.

In C#:

public static int BufferSize
{
    get
    {
        int bufferSize;
        if (Int32.TryParse(ConfigurationManager.AppSettings ["uploadUtilsBufferSize"], out bufferSize))
        {
            return bufferSize;
        }
        return DEFAULT_BUFFER_SIZE;
    }
}
  1. Configure the upload path. Add the following code which defines the static UploadPath property. This property returns the fileUploadPath application setting.

In C#:

public static string UploadPath
{
    get
    {
        string path = DEFAULT_UPLOAD_PATH;
        if (ConfigurationManager.AppSettings["fileUploadPath"] != null)
        {
            path = ConfigurationManager.AppSettings["fileUploadPath"];
        }
        if (path.StartsWith("~"))
        {
            path = HttpContext.Current.Server.MapPath(path);
        }
        if (!path.EndsWith(Path.DirectorySeparatorChar.ToString()))
            path += Path.DirectorySeparatorChar;
        if (!Directory.Exists(path))
            throw new DirectoryNotFoundException(path);
        return path;
    }
}
  1. Configure the method for retrieving the stream from the cache.

Define the GetStreamFromCache static method. This static method returns the MemoryStream by a given key. To do this it uses a Dictionary<string, MemoryStream> object into the ASP.NET cache. While the file is uploading part of its data is kept for a while in this dictionary. When a threshold is reached (the one defined in UploadUtils.BufferSize) this data chunk is appended to the file data on the disk. As you are going to see in the Handle WebUpload events section, that will make it possible not to keep the whole file into the cache. Instead, it will write the data to the file on disk by chunks which size is defined by the UploadUtils.BufferSize property.

In C#:

public static MemoryStream GetStreamFromCache(string key)
{
    Dictionary<string, MemoryStream> uploadingFiles = HttpContext.Current.Cache["UploadingFiles"] as Dictionary<string, MemoryStream>;
    MemoryStream stream;
    if (uploadingFiles == null)
    {
        uploadingFiles = new Dictionary<string, MemoryStream>();
        HttpContext.Current.Cache["UploadingFiles"] = uploadingFiles;
    }
    if (!uploadingFiles.ContainsKey(key))
    {
        stream = new MemoryStream();
        uploadingFiles.Add(key, stream);
    }
    else
    {
        stream = uploadingFiles[key];
    }
    return stream;
}
  1. Configure the method for removing the stream from the cache.

Define the RemoveStreamFromCache static method. This method uses a key to removes an item form the dictionary. As you are going to see in the Handle WebUpload events section, you will use it to remove the file from the currently uploading files cache.

In C#:

public static void RemoveStreamFromCache(string key)
{
    Dictionary<string, MemoryStream> uploadingFiles = HttpContext.Current.Cache["UploadingFiles"] as Dictionary<string, MemoryStream>;
    if (uploadingFiles != null && uploadingFiles.ContainsKey(key))
    {
        uploadingFiles.Remove(key);
    }
}
  1. Configure the method to write the stream to a file.

Define the AppendStreamToFile static method. This static method writes a MemoryStream object to a file. The method accepts as parameters a file name and the MemoryStream value.

In C#:

public static void AppendStreamToFile(string fileName, MemoryStream stream)
{
    using (FileStream fs = new FileStream(fileName, FileMode.Append))
    {
        stream.WriteTo(fs);
    }
}

4. Handle WebUpload events.

1. Client side events

  1. Add OnError handler

    1. Open “Default.aspx” file

    2. Select the WebUpload1 control

    3. Open the Properties window (Use F4 key)

    4. Expand the “ Client Events ” node

    5. Double click in field on the right of OnError field (Alternatively you can select the drop down button and choose the “Add new handler…” option.)

    6. Confirm the dialog – a dialog “Add new client side event handler pops up. Press OK to confirm it.

    7. Implement OnError handler

In JavaScript:

function WebUpload1_OnError(eventArgs, infoObject) {
    if (infoObject.errorType === "serverside")
        $("#uploadErrors").append("<p>" + infoObject.serverMessage + "</p>");
    else
        $("#uploadErrors").append("<p>" + infoObject.errorMessage + "</p>");
}

This OnError client side handler appends the errors to a div with id uploadErrors which we defined earlier.

There are two types of errors server-side and client-side, which can be distinguished by the infoObject.errorType property.

If infoObject.errorType == “serverside then the error originated in developer code on the server. The error message is retained in infoObject.serverMessage.

If infoObject.errorType == “clientside then the error originated on the client side. Its message is kept in infoObject.errorMessage. For more information see the Using Client- Side Events topic.

2. Server side events

  1. Add using clauses to Default.aspx.cs file

In C#:

using System.IO;
using Infragistics.Web.UI.EditorControls;

They are used by the MemoryStream class and UploadStatus enum respectively.

  1. Add FileUploading handler

    1. Open “Default.aspx” file

    2. Select the WebUpload1 control

    3. Open the Properties window (Use F4 key)

    4. Open the Events tab by clicking the Events button

    5. Double click in field on the right of FileUploading field.

    6. Implement FileUploading handler

In C#:

protected void WebUpload1_FileUploading(object sender, Infragistics.Web.UI.EditorControls.FileUploadingEventArgs e)
{
    MemoryStream stream = UploadUtils.GetStreamFromCache(e.TemporaryFileName);
    stream.Write(e.FileChunk, 0, e.FileChunk.Length);
    if (stream.Length >= UploadUtils.BufferSize)
    {
        string fileName = UploadUtils.UploadPath + e.TemporaryFileName;
        stream.Flush();
        UploadUtils.AppendStreamToFile(fileName, stream);
        stream.Close();
        UploadUtils.RemoveStreamFromCache(e.TemporaryFileName);
    }
    e.Cancel = true;
}

The purpose of the FileUploading event is to allow developer to manually handle the data upload when FileSaveType application setting is set to memorystream . For this purpose FileUploadingEventArgs class contains a FileChunk property which holds the uploaded chunk of data as a byte array.

It is very important to note that several files can be uploaded at one time; consequently, this event can rise asynchronously for the files. That is why you should employ a method of recognizing which file the incoming data is coming. One approach is to look into FileUploadingEventArgs.TemporaryFileName property which guarantees a unique name for a file. In the FileUploading handler we use TemporaryFileName as a key to write file data to the ASP.NET cache by using the UploadUtils.GetStreamFromCache static method.

Our FileUploading handler also writes the file data to a file if the stream length exceeds the UploadUtils.BufferSize threshold. This way we ensure that the computer RAM is utilized.

  1. Add FileFinishing handler

    1. Repeat the steps from section B in order to define FileFinishing handler

    2. Implement FileFinishing handler

In C#:

protected void WebUpload1_UploadFinishing(object sender, Infragistics.Web.UI.EditorControls.UploadFinishingEventArgs e)
{
    string uploadFilePath = UploadUtils.UploadPath;
    string fileName = UploadUtils.UploadPath + e.TemporaryFileName;
    MemoryStream stream = UploadUtils.GetStreamFromCache(e.TemporaryFileName);
    UploadUtils.AppendStreamToFile(fileName, stream);
    stream.Close();
    UploadUtils.RemoveStreamFromCache(e.TemporaryFileName);
    string newFileName = UploadUtils.UploadPath + e.FileName;
    if (System.IO.File.Exists(newFileName))
        System.IO.File.Delete(newFileName);
    System.IO.File.Move(fileName, newFileName);
}

The UploadFinishing handler writes the last chunk of the file which is still in the cache and renames the file to its original name. If there is an existing file in the same directory it is deleted and the new file is written.

Note
Note:

In a real world scenario you will not override files because this is an issue. Usually you will use a username or some other identifier to ensure that files do not override each other; however, all of this will be a function of your particular business implementation.

  1. Add FileFinished handler

    1. Repeat the steps from section B in order to define FileFinished handler

    2. Implement FileFinished handler

In C#:

protected void WebUpload1_UploadFinished(object sender, Infragistics.Web.UI.EditorControls.UploadFinishedEventArgs e)
{
    switch (e.FileStatus)
    {
        case UploadStatus.CancelledByClientCommand:
        case UploadStatus.CancelledByClientConnection:
            // delete the file if the upload was unsuccessful
            System.IO.File.Delete(e.TemporaryFileName);
            break;
    }
}

We implement UploadFinished scenarios in order to handler situations when user cancels the upload. This implementation deletes the temporary file in which we store file data.

5. (Optional) Verify the result.

To verify the result, build and run the project . Upload a file and check if it is available in the temporary uploaded files folder.