今天分享一个自己手动搭建文件服务器,本来想上网找个现成的直接用的,结果发现太麻烦,级别太重,不如自己写的快,一天写测,第二天上线使用。

如下图,下面是调用的一个测试使用的界面。



测试上传和下载的功能。

基本原理说一下:


1.客户端上传file,转换成二进制流到服务器,服务器接收进行MD5加密,把文件及密码存入文件服务器库,文件根据MD5保存进本地文件夹,文件夹命名第一级取MD5前二位,第二级文件目录是MD5第3和4位,保存的文件重新命名,名称是当前加密的MD5。
当然,加密储存需要验证的,如果本地已经存了这个MD5就认为已经保存了相同的文件,就不需要再保存。

2.下载文件的时候 直接通过该MD5取文件即可。



 上图是基本流程,逻辑上还是有漏洞,实际上又有改动。基本流程是这样了,可以大概看看,懒得再划一个图了。

服务端结构:

 



 

FileService.asmx 提供服务,核心代码在FileCoreService.cs. 本项目用的Dapper,简单方便、实用。

WebApplication1 就是测试用的,客户端调用的了。

WFBPMFile 可以忽略了,我的一个转换功能,原先文件是文件流存入数据库里的,大概100G,然后转换成文件,放入文件服务器了。

 

核心代码 放出来吧,喜欢的可以拿去.
1 using FZ.File.Dapper; 2 using System; 3 using System.Collections.Generic;
4 using System.Data; 5 using System.Linq; 6 using System.Diagnostics; 7
using System.IO; 8 using System.Security.Cryptography; 9 using System.Text;
10 11 namespace FZ.File.Logic 12 { 13 public class FileCoreService 14 { 15
/// <summary> 16 /// 根据文件名和MD5检查是否存在, 检查文件名和MD5都存在 17 /// </summary> 18 ///
<param name="filename">文件名</param> 19 /// <param name="md5str">文件流加密的MD5
</param> 20 /// <returns></returns> 21 public bool CheckFilNameMD5(string
filename,string md5str) 22 { 23 using (IDbConnection conn =
DBConfig.GetSqlConnection()) 24 { 25 try 26 { 27 string sql = "SELECT
COUNT(*) FROM BPM_tb_UploadFile WHERE [FileName]=@FileName AND FileMD5=@FileMD5"
; 28 //sql = String.Format(sql,filename,md5str); 29 //var count =
conn.ExecuteScalar(sql, null, null, null, CommandType.Text); 30 var param = new
DynamicParameters(); 31 param.Add("@FileName", filename); 32 param.Add("
@FileMD5", md5str); 33 var count = conn.ExecuteScalar(sql, param, null, 3600,
CommandType.Text); 34 if ((int)count > 0) 35 { 36 return true; 37 } 38 }
39 catch (Exception ex) 40 { 41 throw ex; 42 } 43 } 44 return false;
45 } 46 47 /// <summary> 48 /// 验证数据的完整性(接收到的文件流MD5与接收到的MD5验证) 49 ///
</summary> 50 /// <param name="md5str">接收的MD5</param> 51 /// <param
name="sourceStream">文件流</param> 52 /// <returns></returns> 53 public bool
CheckMD5(string md5str, System.Byte[] sourceStream) 54 { 55 var jmd5 =
GetMD5HashByByte(sourceStream); 56 if (md5str == jmd5) 57 { 58 return true;
59 } 60 return false; 61 } 62 public bool InsertFile(System.Byte[]
sourceStream,string md5str,string filename) 63 { 64 bool sf =
SaveFileToDisk(sourceStream,"D:\\UploadFile\\", md5str); //先保存文件 65 if (sf) 66
{ 67 //TO DO 插入数据库 68 using (IDbConnection conn = DBConfig.GetSqlConnection())
69 { 70 try 71 { 72 string sql = "INSERT INTO
BPM_tb_UploadFile([FileName],[FileMD5],[FileSize],[Description])
VALUES('{0}','{1}',{2},'{3}')"; 73 sql = String.Format(sql, filename, md5str,
sourceStream.Length,""); 74 var count = conn.Execute(sql, null, null, null,
CommandType.Text); 75 //var param = new DynamicParameters(); 76 //
param.Add("@FileName", filename); 77 //param.Add("@FileMD5", md5str); 78 //var
count = conn.Execute(sql, param, null, 3600, CommandType.Text); 79 if (count >
0) 80 { 81 return true; 82 } 83 } 84 catch (Exception ex) 85 { 86
throw ex; 87 } 88 } 89 } 90 return false; 91 } 92 // 根据二进制流生成MD5 93
private string GetMD5HashByByte(System.Byte[] sourceStream) 94 { 95 MD5 md5 =
new MD5CryptoServiceProvider(); 96 byte[] result =
md5.ComputeHash(sourceStream); 97 String ret = ""; 98 for (int i = 0; i <
result.Length; i++) 99 ret += result[i].ToString("x").PadLeft(2, '0'); 100
return ret; 101 } 102 103 // 根据文件流生成MD5(与上一方法生成结果相同) 104 private string
GetMD5HashByFile(string fileName) 105 { 106 FileStream file = new
FileStream(fileName, FileMode.Open);107 MD5 md5 = new
MD5CryptoServiceProvider();108 byte[] result = md5.ComputeHash(file); 109
file.Close();110 StringBuilder sb = new StringBuilder(); 111 for (int i = 0; i
< result.Length; i++) 112 { 113 sb.Append(result[i].ToString("x2")); 114 } 115
return sb.ToString(); 116 } 117 118 // 保存文件流到服务器上指定位置 119 private bool
SaveFileToDisk(System.Byte[] sourceStream,string fileFullName) 120 { 121 bool
result =false; 122 try 123 { 124 //待保存的路径 125 string savePath =
Path.GetDirectoryName(fileFullName);126 if (!Directory.Exists(savePath)) 127 {
128 Directory.CreateDirectory(savePath); 129 } 130 131 using (FileStream
fsTarget =new FileStream(fileFullName, FileMode.Create, FileAccess.Write,
FileShare.None))132 { 133 fsTarget.Write(sourceStream, 0, sourceStream.Length);
134 fsTarget.Flush(); 135 fsTarget.Close(); 136 result = true; 137 } 138 }
139 finally 140 { 141 } 142 return result; 143 } 144 145 private bool
SaveFileToDisk(System.Byte[] sourceStream,string filepath,string md5) 146 { 147
bool result = false; 148 string fileFullName = filepath + md5.Substring(0, 2) +
"\\" + md5.Substring(2, 2)+"\\" + md5; 149 try 150 { 151 //待保存的路径 152 string
savePath = Path.GetDirectoryName(fileFullName); 153 if (!
Directory.Exists(savePath))154 { 155 Directory.CreateDirectory(savePath); 156
}157 158 using (FileStream fsTarget = new FileStream(fileFullName,
FileMode.Create, FileAccess.Write, FileShare.None))159 { 160
fsTarget.Write(sourceStream,0, sourceStream.Length); 161 fsTarget.Flush(); 162
fsTarget.Close();163 result = true; 164 } 165 } 166 finally 167 { 168 } 169
return result; 170 } 171 172 public System.Byte[] ReadFileByte(string filename,
string md5str) 173 { 174 var filepath = "D:\\UploadFile\\" + md5str.Substring(0
,2) + "\\" + md5str.Substring(2, 2) + "\\" + md5str; 175 FileStream fileStream =
new FileStream(filepath, FileMode.Open); 176 byte[] bytes = new byte
[fileStream.Length];177 fileStream.Read(bytes, 0, bytes.Length); 178
fileStream.Close();179 return bytes; 180 } 181 public FileStream
ReadFileStream(string filename, string md5str) 182 { 183 var filepath = "
D:\\UploadFile\\" + md5str.Substring(0, 2) + "\\" + md5str.Substring(2, 2) + "\\
" + md5str; 184 FileStream fileStream = new FileStream(filepath, FileMode.Open);
185 fileStream.Close(); 186 return fileStream; 187 } 188 189 190 } 191 }
 

上面保存的文件路径自己写入配置文件吧,还有日志文件路径,自己到配置文件改一下。代码写的很烂,各位高人忽略即可。

 

提供的服务代码:

 
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4
using System.Web; 5 using System.Web.Services; 6 using FZ.File.Logic; 7 using
System.IO; 8 9 namespace BPMFileService 10 { 11 /// <summary> 12 ///
FileService 的摘要说明13 /// </summary> 14 [WebService(Namespace = "
http://tempuri.org/")] 15 [WebServiceBinding(ConformsTo =
WsiProfiles.BasicProfile1_1)]16 [System.ComponentModel.ToolboxItem(false)] 17 //
若要允许使用 ASP.NET AJAX 从脚本中调用此 Web 服务,请取消注释以下行。18 //
[System.Web.Script.Services.ScriptService] 19 public class FileService :
System.Web.Services.WebService20 { 21 22 [WebMethod] 23 public bool CheckMD5(
string filename,string md5str) 24 { 25 FileCoreService fs = new
FileCoreService();26 return fs.CheckFilNameMD5(filename, md5str); 27 } 28
[WebMethod]29 public bool InsertFile(System.Byte[] FileStream,string filename,
string md5str) 30 { 31 FileCoreService fs = new FileCoreService(); 32 bool b =
fs.CheckMD5(md5str, FileStream);//验证MD5 33 if (b) 34 { 35 b =
fs.InsertFile(FileStream, md5str,filename);//保存文件,并更新到数据库 36 if (b) {
LocalLog.Write("插入文件成功,文件名:" + filename + " MD5:" + md5str); } 37 else {
LocalLog.Write("插入文件失败,文件名:" + filename + " MD5:" + md5str); } 38 } 39 else 40
{41 LocalLog.Write("接收的文件不完整,请检查!文件名:" + filename + " MD5:" + md5str); 42 } 43
return b; 44 } 45 [WebMethod] 46 public Byte[] ReadFile(string filename,
string md5str) 47 { 48 FileCoreService fs = new FileCoreService(); 49 Byte[]
bytes = fs.ReadFileByte(filename, md5str); 50 LocalLog.Write("读取文件 NAME:" +
filename +" MD5:" + md5str); 51 return bytes; 52 } 53 } 54 }
 

客户端上传调用的代码:
1 protected void btnUp_Click(object sender, EventArgs e) 2 { 3
FileServiceSoapClient fsclient =new FileServiceSoapClient(); 4 byte[] fb =
FileUpload1.FileBytes; 5 System.IO.Stream s =
FileUpload1.PostedFile.InputStream; 6 var md5str = GetMD5HashByByte(fb); 7 var
md5str2 = GetMD5HashByFile(s); 8 9 var filename = FileUpload1.FileName; 10
bool b = fsclient.CheckMD5(filename, md5str); 11 if (!b) 12 { 13 if (md5str ==
md5str2) {14 b = fsclient.InsertFile(fb, filename, md5str); 15 } 16 } 17 }
 

客户端下载的代码:
protected void btndown_Click(object sender, EventArgs e) {
FileServiceSoapClient fsclinent= new FileServiceSoapClient(); var Dbytes =
fsclinent.ReadFile("新建文本文档.txt", "450ccb8dc556e010ff95b787084d2c51"); //byte[]
bytes =byte.Parse(Dbytes.ToString()): Response.ContentType = "
application/octet-stream;charset=gb2321"; //通知浏览器下载文件而不是打开;对中文名称进行编码
Response.AddHeader("Content-Disposition", "attachment; filename=" +
HttpUtility.UrlEncode("新建文本文档.txt", System.Text.Encoding.UTF8));
Response.BinaryWrite(Dbytes); Response.Flush(); Response.End(); }
 数据库也比较简单:



 

日志:





 

交流的企鹅群:14615476  ,群员免费提供源码。

 

友情链接
KaDraw流程图
API参考文档
OK工具箱
云服务器优惠
阿里云优惠券
腾讯云优惠券
华为云优惠券
站点信息
问题反馈
邮箱:[email protected]
QQ群:637538335
关注微信