Changing names in file uploads
Quite a few people have been using the ASP.Net file upload module and have been asking if it is possible to save the uploaded files to different locations or with custom file names.
This is possible by changing your implementation of the IFileProcessor interface, specifically the StartNewFile method.
public interface IFileProcessor:IDisposable
{
void StartNewFile(string fileName, string contentType);
void Write(byte[] buffer, int offset, int count);
void EndFile();
string GetFileName();
}
This method is called when file processing begins and before the first byte has been streamed to the destination file. Here you create your “container” for the saved bytes- a physical file, database row, etc. Although the filename specified by the user is passed in you can override this and name the file anything you like.
To demonstrate, let’s say we want to prefix all file names with “upload_”. A trivial example I know, but it demonstrates the point. Take a look at FileSystemProcessor.cs in the solution. This is the default processor, which simply streams the uploaded bytes into a file.
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
namespace DJ.Blog.FileUpload
{
/// <summary>
/// Implements the IFileProcessor interface to stream uploaded files to
/// a directory in the file system.
/// </summary>
public class FileSystemProcessor : IFileProcessor
{
FileStream _fs;
string _outputPath;
string _fileName;
/// <summary>
/// Gets/sets the output folder path.
/// </summary>
public string OutputPath
{
get { return _outputPath; }
set
{
value = value.Trim();
if (!value.EndsWith(@"\")) value += @"\";
if (!Directory.Exists(value))
throw new ArgumentException("Directory does not exist:" + value);
_outputPath = value;
}
}
/// <summary>
/// Constructor.
/// </summary>
public FileSystemProcessor()
{
// Default to the root of the web application
_outputPath = System.Web.HttpContext.Current.Server.MapPath("~/");
}
/// <summary>
/// Starts a new file.
/// </summary>
/// <param name="fileName">File name.</param>
/// <param name="contentType">The content type of the file.</param>
public void StartNewFile(string fileName, string contentType)
{
_fileName = fileName;
_fs = new FileStream(_outputPath + Path.GetFileName(fileName), FileMode.Create);
}
/// <summary>
/// Writes to the output file.
/// </summary>
/// <param name="buffer">Buffer to write from.</param>
/// <param name="offset">Offset in the buffer to write from.</param>
/// <param name="count">Count of bytes to write.</param>
public void Write(byte[] buffer, int offset, int count)
{
_fs.Write(buffer, offset, count);
}
/// <summary>
/// Ends current file processing.
/// </summary>
public void EndFile()
{
_fs.Flush();
_fs.Close();
_fs.Dispose();
}
/// <summary>
/// Returns the name of the file that is currently being processed.
/// Null if there is no file.
/// </summary>
/// <returns>The file name.</returns>
public string GetFileName()
{
return _fileName;
}
/// <summary>
/// Dispose of the object.
/// </summary>
void IDisposable.Dispose()
{
if (_fs != null)
{
_fs.Dispose();
}
}
}
}
We can create a new file processor (FileSystemProcessor2.cs) and implement the StartNewFile method as follows, simply adding in our prefix.
/// <summary>
/// Starts a new file.
/// </summary>
/// <param name="fileName">File name.</param>
/// <param name="contentType">The content type of the file.</param>
public void StartNewFile(string fileName, string contentType)
{
_fileName = fileName;
_fs = new FileStream(_outputPath + "upload_" + Path.GetFileName(fileName), FileMode.Create);
}
In a similar way, the output folder can also be changed (perhaps to save uploaded files depending on the login name of the current user or to organise by date).
The final step is to register the processor with the upload module. This is done in the application_start event in the global.asax file of the web application.
void Application_Start(object sender, EventArgs e)
{
// Set up the file processor for the upload manager
UploadManager.Instance.ProcessorType = typeof(FileSystemProcessor2);
UploadManager.Instance.ProcessorInit += new FileProcessorInitEventHandler(Processor_Init);
}
void Processor_Init(object sender, FileProcessorInitEventArgs args)
{
FileSystemProcessor2 processor;
processor = args.Processor as FileSystemProcessor2;
if (processor != null)
{
// Set up the download path here - default to the root of the web application
processor.OutputPath = @"c:\uploads";
}
}
I hope this helps to shed some light onto how custom file processors can be created.

Comment by OutOfTouch on 30 June 2008:
How to save to a relative path? Server.MapPath doesn’t work here.
I don’t see from the example how to save to relative path.
Comment by OutOfTouch on 30 June 2008:
I guess I can use System.Web.HttpContext.Current.Server.MapPath to change the path that uploaded files are going to.