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






4.91/5 (20投票s)
将您的数据库表映射为网络磁盘。
引言
每次我使用 Linux 时,看到它的文件系统我都会感到一丝嫉妒。不仅有 NTFS、FAT32,还有包括 GDocs 等在内的各种各样的数据存储。现在我也可以扬眉吐气了!Dokan 太棒了!
什么是 Dokan?
什么是 Dokan?简单来说,它是一个封装了文件系统调用的设备驱动。使用代理函数,可以以编程方式为 ReadFile
、WriteFile
、FindFiles
等核心 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 或任何您想存储数据的数据库。
所有代码均按原样发布。对于由此可能带来的任何不便,我深表歉意。时间过得太快,我还有其他事情要做,而不仅仅是这个项目。
请享用这段代码,如果您想认真参与这个项目,请与我联系。一些使用云的例子会很好。
历史
这是第一个版本,概念验证。