问题来源
文件服务器文件夹操作在PC与Surface端确实还是和传统操作一样没什么区别,但是到了手机端与Surface Hub就没那么方便了,因为文件资源管理器Explorer根本没法添加网路硬盘。对于传统行业以及担心云端文件安全的企业固然还是希望能使用文件服务器,特别是SurfaceHub用户。SurfaceHub用户开会的时候会用到很多敏感文件,这个时候如果无法访问内部机密文件服务器,那还有什么用处?
解决方法
由于SurfaceHub只能安装UWP应用,自然首先考虑UWP的实现。目前支持SurfaceHub的应用只有Yuki Explorer与Metro commander,Total Commander暂时不支持。两者也许是使用了以下提到的开源项目SharpCifs??
方法一:文件类型关联(只支持单个用户与Guest用户)
文件服务器文件路径其实就是UNC路径,在StorageFolder说明里面有如下文字:
实现:
1,添加权限
2,添加文件类型关联
3,文件服务器文件操作(必须事先将文件服务器的认证信息保存在Window凭据里)
public class ShareFolderService { /// <summary> /// get items from unc path /// *you must add the /// </summary> /// <param name="uncPath"></param> /// <returns></returns> public async Task<IReadOnlyList<IStorageItem>> GetShareFolderItemsAsync(string uncPath) { var folder = await getFolderFromUncPath(uncPath); return await folder.GetItemsAsync(); } /// <summary> /// create item to share folder /// </summary> /// <param name="uncPath"></param> /// <param name="name"></param> /// <param name="isFile"></param> /// <returns></returns> public async Task<IStorageItem> ShareFolderCreateItemAsync(string uncPath, string name, bool isFile = true) { var shareFolder = await getFolderFromUncPath(uncPath); if (isFile) { return await shareFolder.CreateFileAsync(name, CreationCollisionOption.ReplaceExisting); } else { return await shareFolder.CreateFolderAsync(name, CreationCollisionOption.OpenIfExists); } } private async Task<StorageFolder> getFolderFromUncPath(string uncPath) { return await StorageFolder.GetFolderFromPathAsync(uncPath); } }
缺点:
- 用户固定无法自由切换
- 无法查看全部文件
- 在SurfaceHub端将导致双击文件无法启动(SurfaceHub Bug)
方法二:SMB协议实现(支持全部用户)
概述:
服务器信息块(SMB)协议是一种IBM协议,用于在计算机间共享文件、打印机、串口等。SMB 协议可以用在因特网的TCP/IP协议之上,也可以用在其它网络协议如IPX和NetBEUI 之上。
SMB 一种客户机/服务器、请求/响应协议。通过 SMB 协议,客户端应用程序可以在各种网络环境下读、写服务器上的文件,以及对服务器程序提出服务请求。此外通过 SMB 协议,应用程序可以访问远程服务器端的文件、以及打印机、邮件槽(mailslot)、命名管道(named pipe)等资源。
在 TCP/IP 环境下,客户机通过 NetBIOS over TCP/IP(或 NetBEUI/TCP 或 SPX/IPX)连接服务器。一旦连接成功,客户机可发送 SMB 命令到服务器上,从而客户机能够访问共享目录、打开文件、读写文件,以及一切在文件系统上能做的所有事情。
从 Windows 95 开始,Microsoft Windows 操作系统(operating system)都包括了客户机和服务器 SMB 协议支持。Microsoft 为 Internet 提供了 SMB 的开源版本,即通用 Internet 文件系统 (CIFS)。与现有 Internet 应用程序如文件传输协议(FTP)相比, CIFS 灵活性更大。对于 UNIX 系统,可使用一种称为 Samba 的共享软件。
Windows的Explorer实现原理如下:
Windows系统本身就是一个SMB服务器,支持版本如下:
除了Windows系统以外,UNIX与Mac或者Android系统都可以安装SMB软件访问各类文件服务器系统的文件。
JAVA开源项目:JCIFS
JCIFS 是一个纯 JAVA 编写的实现 CIFS/SMB 协议的开源项目。它由 samba 组织负责维护开发。 JCIFS 是一个完整的,丰富的,具有可扩展能力且线程安全的客户端库。这一库可以应用于各种 JAVA 虚拟机访问遵循 CIFS/SMB 网络传输协议的网络资源,包括 Windows 下的共享资源和 Linux & Unix 下的 SAMBA 资源。
.NET开源项目:
- SharpCifs :从JCIFS转换而来,支持Windows Phone 8.1 (Silverlight) 。
- SharpCifs.Std :从SharpCifs转换而来,支持Xamarin & .NET Core 。Nuget包:Install-Package SharpCifs.Std –Pre
实现:
主要使用SharepCifs.Std来实现。由于SharepCifs.Std类库使用了UWP不支持的包:
- System.Console (>= 4.3.0)
System.Threading.Thread (>= 4.3.0)
所有需要将以上两个包做替换处理:
- System.Console (>= 4.3.0)关联的Console.Error与Console.Write方法替换为StringWriter的Write方法
- System.Threading.Thread (>= 4.3.0) 替换成Task方法(CurrentThread方法可以为空)
- Dns.GetHostName()需要替换回SharepCifs的方法
读取文件夹列表:
var folder = new SmbFile("smb://UserName:Password@ServerName/ShareName/Folder/")); var epocDate = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); foreach (var item in folder.ListFiles()) { var lastModDate = epocDate.AddMilliseconds(item.LastModified()).ToLocalTime(); Log.WriteLine($"Name: {item.GetName()}, isDir?: {item.IsDirectory()}, Date: {lastModDate.ToString("yyyy-MM-dd HH:mm:ss")}"); }
读取文件:
var file = new SmbFile("smb://UserName:Password@ServerName/ShareName/Folder/FileName.txt")); var readStream = file.GetInputStream(); var buffer = new byte[1024*8]; var memStream = new MemoryStream(); int size; while ((size = readStream.Read(buffer, 0, buffer.Length)) > 0) memStream.Write(buffer, 0, size); Log.WriteLine(Encoding.UTF8.GetString(memStream.ToArray()));
创建文件:
var file = new SmbFile("smb://UserName:Password@ServerName/ShareName/Folder/NewFileName.txt")); file.CreateNewFile(); var writeStream = file.GetOutputStream(); writeStream.Write(Encoding.UTF8.GetBytes("Hello!"));
优点:
- 由于需要用户名与密码所有支持所有用户(含Guest)
- 支持所有设备(SurfaceHub,Android,iOS等)
- 支持所有文件类型
备注:
目前打算将次代码转换为PCL类库,以供在Xamarin项目中实现。