65.9K
CodeProject 正在变化。 阅读更多。
Home

用于 Microsoft-SQL 的 FUSE(用户空间文件系统),使用 C#

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.91/5 (20投票s)

2010年8月19日

CPOL

4分钟阅读

viewsIcon

53920

downloadIcon

1

将您的数据库表映射为网络磁盘。

引言

每次我使用 Linux 时,看到它的文件系统我都会感到一丝嫉妒。不仅有 NTFS、FAT32,还有包括 GDocs 等在内的各种各样的数据存储。现在我也可以扬眉吐气了!Dokan 太棒了!

什么是 Dokan?

什么是 Dokan?简单来说,它是一个封装了文件系统调用的设备驱动。使用代理函数,可以以编程方式为 ReadFileWriteFileFindFiles 等核心 I/O 操作创建响应。甚至可以在 .NET 中使用!还有 Dokan.NET 接口供 C# 使用。
现在您可以编写自己的文件系统了。您可以创建自己的文件系统,也可以从使用 Dokan 的其他应用程序中获得灵感。使用内存作为 RAMDisk 的文件系统怎么样?使用加密文件作为数据存储的系统?镜像某个磁盘或目录,通过文件系统访问注册表,将进程列表作为文件放在附加磁盘上?打开您的思路,寻找 Dokan 的其他用途。本文将帮助您。您将能够将文件存储在 Microsoft SQL 数据库中,进行复制、重命名、更新、删除等操作。简单的文件版本控制也包含在内。这就是我开始玩 Dokan 的原因。创建一个具有版本控制功能的外部存储,并将其挂载为磁盘,方便最终用户使用。要创建文件的版本,只需在文件名末尾添加扩展名 ".version" 并复制到磁盘。文件将被重命名为之前的扩展名,而之前的文件版本将获得一个版本号。您可以选择是显示所有版本还是仅显示当前版本。

先决条件

已安装 Dokan:从 Dokan 官网下载最新版本 (0.53) 并安装。(代码已在 0.52 版本上进行测试。)

准备好的 Microsoft-SQL:用于测试目的,您 PC 上的 Microsoft-SQL Express 版本就足够了,但我使用了位于虚拟机上的 SQL Server 来模拟一些网络流量和网络故障。对于生产环境,请小心并创建一个特殊的用户账户,并允许该账户运行存储过程。不要错过这一步。这是一个好的安全策略。
在 SQL 服务器上,运行元数据脚本来创建 C# 应用程序使用的存储过程,并创建表 "DOKANFS",这是您应用程序使用的主表,也是唯一一个表。如果您想使用其他表,则必须在所有存储过程中重命名所有出现的 "DOKANFS"。

VS 2008 标准版:如果您使用的是 Express 版,则必须在本地机器上使用 Microsoft-SQL Express。Express 版不允许远程连接到 Microsoft-SQL 服务器。

动手实践

让我们看看代码,找出一些有用的东西。正如您所见,文件被“预加载”到一个简单的 Dictionary 对象中。当第一次调用 ReadFile 方法以接收数据时,文件将从 SQL 数据库加载。如果文件被压缩过,则会下载并解压缩。文件在写入 SQL Server 时会被压缩,以减少网络流量。但是,如果文件具有列出的扩展名,则不会被压缩。
文件通过 FileCache 对象进行读取和写入。文件关闭时,将调用 Dokan.NET 调用的 Cleanup 过程。Cleanup 过程执行数据的压缩,并调用 SP WriteFile 将数据存储到 Microsoft-SQL 中。

 public int Cleanup(string filename, DokanFileInfo info)
        {
            lock (FileCache)
            {
                if ((FileCache.ContainsKey(filename) == true) && 
			(FileCache[filename].MemStream.Length > 0))
                {
                    using (SqlConnection conn = new SqlConnection(ConnectionString))
                    {
                        using (SqlCommand Cmd = new SqlCommand())
                        {
                            MemoryStream mem = 
				((FileCaching)FileCache[filename]).MemStream;
                            Cmd.CommandText = "WriteFile";
                            Cmd.Parameters.Add("@iszipped", SqlDbType.Bit, 1);
                            Cmd.Parameters["@iszipped"].Value = 0;
                            Cmd.Parameters.Add("@OriginalSize", SqlDbType.BigInt);
                            Cmd.Parameters["@OriginalSize"].Value = mem.Length;

                            if (this.ZippedExtension.ToLower().IndexOf
				(Path.GetExtension(Regex.Split(filename.ToLower(), 
				".version")[0])) == -1)
                            {
                                if (FileCache[filename].MemStream.Length > 256)
                                {
                                    Cmd.Parameters["@iszipped"].Value = 1;
                                    MemoryStream dummy = new MemoryStream();
                                    Compress(mem, dummy);
                                    mem.SetLength(0);
                                    dummy.WriteTo(mem);
                                }
                            }

                            mem.Seek(0, SeekOrigin.Begin);
                            Cmd.Parameters.Add("@data", SqlDbType.VarBinary, 
					(int)mem.Length);
                            Cmd.Parameters["@data"].SqlValue = mem.ToArray();

                            Cmd.Parameters.AddWithValue("@filename", filename);

                            Cmd.CommandType = CommandType.StoredProcedure;
                            Cmd.Connection = conn;
                            conn.Open();

                            Cmd.ExecuteNonQuery();
                            FileCache.Remove(filename);
                        }
                    }
                }
            };
            return DokanNet.DOKAN_SUCCESS;
        }

在 Microsoft-SQL 上用 T-SQL 编写的代码并不复杂。最复杂的存储过程是 FindFiles,因为它涉及到版本管理。
驱动器是否显示旧版本文件的标志设置在根目录 "\" 的 content 字段的最高位。如果最高位设置为 1,则该过程会在扩展名中显示前一个版本。

    ALTER PROCEDURE [dbo].[FindFiles]
	(
	@filename varchar(255)
	)
	
AS
    /*
    exec FindFiles @filename = '\' 
    */
	SET NOCOUNT ON 
	
	if @filename = '\' set @filename = '\' else set @filename = @filename+'\'
	
	select filename, isdirectory, IsNull(OriginalSize,_
		DATALENGTH([CONTENT])) as size, filename as fullfilename,
	       LastAccessTime,LastWriteTime,CreationTime
	  into #TEMP
	  from DOKANFS 
	 where (filename like @filename+'%' and FILENAME _
		not like @filename+'%\%' and Version is null)
	
	/* all versions */
	declare @allVersion int
	select @allVersion = (isnull(cast(content as int),0) & 1) _
		from DOKANFS where FILENAME = '\'
	
	if @allVersion = 1 begin
	   select filename + '.'+ cast(ISNULL(version,'0') as varchar(10)) as filename, 
	         isdirectory, 
	         IsNull(OriginalSize,DATALENGTH([CONTENT])) as size, 
	         filename+ '.'+ cast(ISNULL(version,'0') as varchar(10)) as fullfilename,
	         LastAccessTime,LastWriteTime,CreationTime
	  into #TEMP2
	  from DOKANFS 
	  where (filename like @filename+'%' and FILENAME _
		not like @filename+'%\%' and Version is not null)
	  update #TEMP2 set filename = SUBSTRING(filename, _
		CHARINDEX(@filename,filename)+LEN(@filename),255)
	  end
	
	
	update #TEMP set filename = SUBSTRING(filename, _
		CHARINDEX(@filename,filename)+LEN(@filename),255)
	
	insert into #TEMP (filename, isdirectory,size,fullfilename,_
		LastAccessTime,LastWriteTime,CreationTime) 
	       values ('.',1,0,'.',GETDATE(),GETDATE(),GETDATE())
	if @filename <> '\' 
	   insert into #TEMP (filename,isdirectory,size,fullfilename,_
		LastAccessTime,LastWriteTime,CreationTime) 
	          values ('..',1,0,'..',GETDATE(),GETDATE(),GETDATE())
	
	if @allVersion = 1 begin
	   select * from #TEMP 
	   union
	   select * from #TEMP2
	   order by filename
	   
	   end else begin
	   
	   select * from #TEMP 
	   order by filename
	   end
	
	RETURN    

关注点

Dokan 0.52 版本工作正常,但在文件被 Notepad++ 打开时,会出现一些意外行为。我仍在等待 Dokan 成长,能够像 TrueCrypt 那样按需加载驱动程序。

缺失的部分

在各种网络上进行大量测试。我在 1Mbit VPN 上测试了这个概念,没有任何问题。这只是一个概念验证,没有进行代码“美化”。还可以实现一些“注册”功能……例如,通过创建所有存储过程、创建表等来准备数据库。您还可以使用 Azure、PostgreSQL、MySQL、Firebird、Oracle 或任何您想存储数据的数据库。
所有代码均按原样发布。对于由此可能带来的任何不便,我深表歉意。时间过得太快,我还有其他事情要做,而不仅仅是这个项目。
请享用这段代码,如果您想认真参与这个项目,请与我联系。一些使用云的例子会很好。

历史

这是第一个版本,概念验证。

© . All rights reserved.