


























Managing files web applications should be quick and easy and most importantly, consistent. The traditional way to store files is on the file system or within a RDBMS (SQL SERVER, Oracle, MySql) or on SharePoint. However what if you were developing several applications that shared a million files and each was 5MB. Which solution would you choose? This is commonly the case for large content driven sites (Music Stores, video sharing sites, content management systems, etc).
In providing a solution I would implement a SOA based File Service, and this article will guide you to making your own File Service. The reasons that I believe a File Service works are:
Firstly we need to create a table to store our file information. Most columns on the table shown in Figure 1 are self explanatory and for possible enhancements see the "Future Enhancements" section below.

Figure 1
Your solution should now look like figure 2 below.

Figure 2
Now that you have created your Data Structure and Visual Studio solution we will need to add the application logic to each of the classes (e.g. FileHandlerFactory.cs).
The FileDistributor class is responsible for distributing files to the file system. It contains a single static method that calculates a network file path for a FileId.
namespace FileServiceCore
{
public static class FileDistributor
{
/// <summary>
/// We use a static variable to tell the system which path to use
/// This ensures the file distributor acts as similar to a Round robin DNS
/// </summary>
private static volatile int pathId = 0;
/// <summary>
/// Gets the distributed file path.
/// </summary>
/// <param name="fileId">The file id.</param>
/// <returns></returns>
public static string GetDistributedFilePath(int fileId)
{
// normally you would store these in a config file or a database, however in keeping this example
// simple I have hard coded the network share paths
string[] networkSharePaths = new string[] {
@"C:\Temp\FileService\Share1\",
@"C:\Temp\FileService\Share2\",
@"C:\Temp\FileService\Share3\"};
// we use an internal variable to ensure there are no multithreading issues
int innerPathId = pathId;
// check that we have a valid path
pathId += 1;
if (pathId >= networkSharePaths.Length)
{
pathId = 0;
}
// check that we have a valid path for the internal variable, as we don't want
// to have a situation where innerPathId doesn't reference a valid network share paths
// array index
if (innerPathId >= networkSharePaths.Length)
{
innerPathId = 0;
}
// check the root path exists and creates the directory if it does not
if (!System.IO.Directory.Exists(networkSharePaths[innerPathId]))
{
System.IO.Directory.CreateDirectory(networkSharePaths[innerPathId]);
}
// pad the fileId to 9 places which allows for about 999,999,999 files
string paddedFileId = fileId.ToString().PadLeft(9, '0');
// check main folder
string mainFolder = System.IO.Path.Combine(networkSharePaths[innerPathId], paddedFileId.Substring(0, 3));
if (!System.IO.Directory.Exists(mainFolder))
{
System.IO.Directory.CreateDirectory(mainFolder);
}
// check the next folder down
string fileFolder = System.IO.Path.Combine(mainFolder, paddedFileId.Substring(3, 3));
if (!System.IO.Directory.Exists(fileFolder))
{
System.IO.Directory.CreateDirectory(fileFolder);
}
// get the final file name (this will be in the xxx.extension format)
string finalFileName = paddedFileId.Substring(6);
// the result should be similar to the following
// C:\Temp\FileService\Share1\
// 000
// 000
// 001.txt
// 002.txt
return System.IO.Path.Combine(fileFolder, finalFileName);
}
}
}
The FileHandlerLinq class is a static class that each calling application can use to Insert/Select files from the Files table. For the purpose of this example update and deletes have been omitted.
Code
FileHandlerFactory.cs
The FileHandlerFactory is a simple factory class that determines if the browser should handle the file or if the file should be downloaded by the client as an attachment. In this example the factory is simple and should be customized for your own needs.
using System;
using System.Web;
namespace FileServiceCore
{
public static class FileHandlerFactory
{
/// <summary>
/// Transmits the file.
/// </summary>
/// <param name="context"><see cref="HttpContext"/>.</param>
/// <param name="fileId">The file id.</param>
public static void TransmitFile(HttpContext context, int fileId)
{
TransmitFile(context, fileId, false);
}
/// <summary>
/// Transmits the file.
/// </summary>
/// <param name="context"><see cref="HttpContext"/>.</param>
/// <param name="fileId">The file id.</param>
/// <param name="forceAsAttachment">if set to <c>true</c> [force as attachment].</param>
public static void TransmitFile(HttpContext context, int fileId, bool forceAsAttachment)
{
File file = FileHandlerLinq.Select(fileId);
if (file != null)
{
// parse the file name and get the extension, we use a ToUpperInvariant
// as there are some characters that cannot be converted to lower case
string extension = file.NetworkPath.Substring(
file.NetworkPath.LastIndexOf(
".", StringComparison.OrdinalIgnoreCase
)
).ToUpperInvariant();
if (forceAsAttachment)
{
extension = string.Empty;
}
// in keeping this example simple we simply parse the file extension to determine if
// we should set the file as an attachment or send it back to the browser
switch (extension)
{
case ".JPG":
case ".JPEG":
case ".PNG":
case ".GIF":
case ".BMP":
TransmitFileToBrowser(context, file);
break;
default:
TransmitFileAsAttachment(context, file);
break;
}
}
}
/// <summary>
/// Transmits the file as attachment.
/// </summary>
/// <param name="context"><see cref="HttpContext"/>.</param>
/// <param name="file">The file entity class.</param>
private static void TransmitFileAsAttachment(HttpContext context, File file)
{
context.Response.AddHeader("content-disposition", "attachment; filename=" + file.FileName);
context.Response.ContentType = file.ContentType;
context.Response.TransmitFile(file.NetworkPath);
}
/// <summary>
/// Transmits the file to browser.
/// </summary>
/// <param name="context"><see cref="HttpContext"/>.</param>
/// <param name="file">The file entity class.</param>
private static void TransmitFileToBrowser(HttpContext context, File file)
{
context.Response.ContentType = file.ContentType;
context.Response.TransmitFile(file.NetworkPath);
}
}
}

DownloadHandler.ashx and ImageHandler.ashx
The Generic ImageHander and DownloadHandlers are almost identical and their sole purpose is to parse the Uri for the FileId token. Once the FileId token is collected the generic handler calls for a file handler from the File Handler Factory.
The below sample .aspx page shows a common usage to add data to the file service.
The following list highlights some of the possible enhancements that can be made to the service, depending on your needs:
本文转载 kane-nina
http://www.kanebarton.com/Samples/FileService/Default.aspx
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。